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
هل يقدر الحاسب يخزن الأرقام عشريًا؟
تمثيل الأعداد
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/
مراجع
- http://steve.hollasch.net/cgindex/coding/ieeefloat.html
- http://www.toves.org/books/float/
- https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
- https://docs.python.org/2/tutorial/floatingpoint.html
- http://floating-point-gui.de/
- https://modernweb.com/what-every-javascript-developer-should-know-about-floating-points/
شكرًا لكم
0.1+0.2 != 0.3!?
By Ibrahim AshShohail
0.1+0.2 != 0.3!?
- 396