Редактиране: Знам, че аритметиката с плаваща запетая не е точна. И аритметиката дори не е мой проблем. Добавката дава резултата, който очаквах. 8099.99975f
не го прави.
Така че имам тази малка програма:
public class Test {
public static void main(String[] args) {
System.out.println(8099.99975f); // 8099.9995
System.out.println(8099.9995f + 0.00025f); // 8100.0
System.out.println(8100f == 8099.99975f); // false
System.out.println(8099.9995f + 0.00025f == 8099.99975f); // false
// I know comparing floats with == can be troublesome
// but here they really should be equal in every bit.
}
}
Написах го, за да проверя дали 8099.99975
е закръглено до 8100
, когато е написано като IEEE 754 с плаваща единица с единична точност. За моя изненада Java го преобразува в 8099.9995
, когато е написан като плаващ литерал (8099.99975f
). Проверих изчисленията си и стандарта IEEE отново, но не можах да намеря никакви грешки. 8100
е също толкова далеч от 8099.99975
, колкото 8099.9995
, но последният бит от 8100
е 0
, което трябва да го направи правилното представяне.
Така че проверих спецификацията на езика Java, за да видя дали съм пропуснал нещо. След бързо търсене открих две неща:
#P5#
#P6#
Забелязах тук, че нищо не е казано за float литералите. Така че си помислих, че float литералите може би са просто двойни, които, когато се преобразуват към float, се закръглят до нула, подобно на прехвърлянето на float към int. Това би обяснило защо 8099.99975f
е закръглено до нула.
Написах малката програма, която можете да видите по-горе, за да проверя моята теория и наистина открих, че при добавяне на два плаващи литерала, които трябва да доведат до 8100
, се изчислява правилният float. (Тук имайте предвид, че 8099.9995
и 0.00025
могат да бъдат представени точно като единични плаващи числа, така че няма закръгляване, което може да доведе до различен резултат) Това ме обърка, тъй като нямаше много смисъл за мен, че плаващите литерали и изчислените плаващи числа се държат по различен начин, така че аз копах в спецификацията на езика още малко и намерих това:
Литералът с плаваща запетая е от тип float, ако има суфикс с ASCII буква F или f [...]. Елементите на типовете float [...] са тези стойности, които могат да бъдат представени с помощта на IEEE 754 32-битови [...] двоични формати с плаваща запетая с единична точност.
Това в крайна сметка заявява, че литералите трябва да бъдат закръглени в съответствие със стандарта IEEE, който в този случай е до 8100
. Така че защо е 8099.9995
?
println
е мръсен лъжец - подозирам, че неочаквано закръгля и изхвърля резултатите/наблюденията от тестовия случай. Опитайте с явен String.format. - person user2864740   schedule 25.11.2013println(String.format("%.20f", v))
трябва да доведе до представителни стойности на дисплея. - person user2864740   schedule 25.11.2013println
беше проблемът, пак бих очаквал8099.9995f + 0.00025f == 8099.99975f
да е вярно. И знам, че аритметиката с плаваща запетая не е точна. Знам, че резултатът ми няма да бъде точно 8099,99975. Но първите две числа са представени точно. Не са закръглени или нещо подобно. Не виждам защо добавянето на две числа не води до същия резултат като преобразуването на десетичния в единичен float. - person IchBinKeinBaum   schedule 25.11.2013new BigDecimal(8099.99975f).toString()
и т.н. Конструкторът BigDecimal и неговият toString() са точни. - person Patricia Shanahan   schedule 25.11.20138099.99975f
се преобразува в най-близката стойност с плаваща запетая, което е число, много малко по-голямо от 8099.9995, поради което е избрано вместо 8100. Въпреки това, когато Javaprintln
преобразува стойности с плаваща запетая в десетични, обикновено не показва всички цифри. Той показва точно толкова цифри, че преобразуването обратно в плаваща запетая показва същата стойност. Така виждате само „8099.995“, но действителната стойност е малко по-голяма. - person Eric Postpischil   schedule 25.11.2013float
, който се използва заprintln
. Формулировката е „Трябва да има поне една цифра, която да представлява дробната част, и отвъд това толкова много, но само толкова, повече цифри, колкото са необходими за уникалното разграничаване на стойността на аргумента от съседните стойности от типfloat
.“ - person Eric Postpischil   schedule 25.11.2013