0.1 + 0.2 != 0.3

مقدمة في الفاصلة العائمة

إبراهيم الشهيل

@ibrasho

إبراهيم الشهيل

 

خريج علوم حاسب من جامعة الملك سعود

 

أعمل في تمكين للتقنيات

 

 

الأجندة

مقدمة ومصطلحات

 

طرق تمثيل / ترميز الأعداد (notation / representation)

 

معيار IEEE-754 (حسابيات الفاصلة العائمة)

 

تمثيل الأعداد في الحاسب

 

وسائل للتعامل مع الأرقام الكسرية

مرت عليك؟

> x = 0.1
> y = 0.2
> x + y
< 0.30000000000000004

وش هو NaN؟

Terminology

Integer

Decimal

Fraction

Fixed-point

Floating-point

 

representation

notation

مصطلحات

عدد صحيح

عدد كسري

كسر

فاصلة ثابتة

فاصلة عائمة

 

تمثيل

ترميز (تنويط)

الترميز العلمي Scientific Notation

0.5 \times 10^1
0.5×1010.5 \times 10^1
0.05 \times 10^2
0.05×1020.05 \times 10^2
500.0 \times 10 ^{-2}
500.0×102500.0 \times 10 ^{-2}
5.0 \times 10 ^0
5.0×1005.0 \times 10 ^0

هل يقدر الحاسب يخزن الأرقام عشريًا؟

تمثيل الأعداد

http://floating-point-gui.de/formats/binary/

IEEE-754

كيف نتعامل مع الكسور؟

أمثلة في PHP , JavaScript , Python, Java

What can I do to avoid this problem?

That depends on what kind of calculations you’re doing.

 

  • If you really need your results to add up exactly, especially when you work with money: use a special decimal.​
     

  • If you just don’t want to see all those extra decimal places: simply format your result rounded to a fixed number of decimal places when displaying it.
     

  • If you have no decimal datatype available, an alternative is to work with integers, e.g. do money calculations entirely in cents. But this is more work and has some drawbacks.

http://floating-point-gui.de/basic/

Use BigDecimal

BigDecimal a = new BigDecimal("0.1");

BigDecimal b = new BigDecimal("0.2");

BigDecimal c = a.add(b);

http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigDecimal.html

  • BCMath Arbitrary Precision Mathematics  extension
    http://php.net/manual/en/book.bc.php
     
  • Use bc* functions to do arithmetics
<?php

$a = '1.234';
$b = '5';

echo bcadd($a, $b);     // 6
echo bcadd($a, $b, 4);  // 6.2340

echo bccomp('1', '2') . "\n";   // -1
echo bccomp('1.00001', '1', 3); // 0
echo bccomp('1.00001', '1', 5); // 1

Alternatives:

composer require litipk/php-bignumbers:0.7
<?php

use \Litipk\BigNumbers\Decimal as Decimal;

/**
 * There are many ways to create Decimal objects.
 */
$ten = Decimal::fromInteger(10);
$two = Decimal::fromString('2.0');

/**
 * At this moment there are few binary operators
 * that we can use with Decimal objects:
 */
$twenty = $ten->mul($two);
$fifty = $twenty->add(Decimal::fromFloat(30.));

https://github.com/Litipk/php-bignumbers

No native language support

 

Rely on libraries:

* BigDecimal.js

* BigNumber.js

 

* Many others...

 

BigDecimal.js:

npm install bigdecimal
var BD = require("BigDecimal")

a = new BD.BigDecimal("123456.123456789012345678901234567890")


b = new BD.BigDecimal("654321.123456789012345678901234567890")

a.add(b)

BigDecimal.js: 

npm install bignumber.js
var BigNumber = require('bignumber.js');

x = new BigNumber(123.4567)

y = BigNumber('123456.7e-3')

z = new BigNumber(x)

x.equals(y) && y.equals(z) && x.equals(z)      // true

x.dividedBy(y).plus(z).times(9).floor()

https://github.com/MikeMcl/bignumber.js/

Use Decimal

from decimal import Decimal

a = Decimal('0.1')

b = Decimal('0.2')

c = a + b # returns a Decimal representing exactly 0.3

https://github.com/MikeMcl/bignumber.js/

مراجع

شكرًا لكم

Made with Slides.com