Как да получа десетичната част на float?

Трябва да извлека десетичната част от число с плаваща запетая, но получавам странни резултати:

float n = 22.65f;
// I want x = 0.65f, but...

x = n % 1; // x = 0.6499996

x = n - Math.floor(n); // x = 0.6499996185302734

x = n - (int)n; // x = 0.6499996

Защо това се случва? Защо получавам тези стойности вместо 0.65?


person Cristian    schedule 16.02.2011    source източник
comment
За всеки друг, объркан от факта, че описанието на въпроса изглежда отговаря на него, цялата тази тема е хората, които обясняват на потребителя, че само защото сте поставили 22,65 като литерал, това не означава, че числото може да се съхранява в двоична форма . Поплавъците нямат безкрайна точност и ако току-що сте отпечатали n, пак ще получите 6.4999....   -  person TrisT    schedule 13.09.2020


Отговори (10)


float има само няколко цифри на точност, така че трябва да очаквате да видите кръгла грешка доста лесно. опитайте double това има по-голяма точност, но все още има грешки при закръгляване. Трябва да закръглите всеки отговор, който получите, за да получите разумен резултат.

Ако това не е желателно, можете да използвате BigDecimal, който няма грешки при закръгляване, но има свои собствени главоболия IMHO.

РЕДАКТИРАНЕ: Може да намерите това за интересно. Стандартният Float.toString() използва минимално закръгляване, но често не е достатъчно.

System.out.println("With no rounding");
float n = 22.65f;
System.out.println("n= "+new BigDecimal(n));
float expected = 0.65f;
System.out.println("expected= "+new BigDecimal(expected));

System.out.println("n % 1= "+new BigDecimal(n % 1));
System.out.println("n - Math.floor(n) = "+new BigDecimal(n - Math.floor(n)));
System.out.println("n - (int)n= "+new BigDecimal(n - (int)n));

System.out.println("With rounding");
System.out.printf("n %% 1= %.2f%n", n % 1);
System.out.printf("n - Math.floor(n) = %.2f%n", n - Math.floor(n));
System.out.printf("n - (int)n= %.2f%n", n - (int)n);

щампи

With no rounding
n= 22.6499996185302734375
expected= 0.64999997615814208984375
n % 1= 0.6499996185302734375
n - Math.floor(n) = 0.6499996185302734375
n - (int)n= 0.6499996185302734375
With rounding
n % 1= 0.65
n - Math.floor(n) = 0.65
n - (int)n= 0.65
person Peter Lawrey    schedule 16.02.2011
comment
+1 Страхотни неща @Peter, благодаря. Някаква идея дали ви е гарантирано, че n - Math.floor(n) ‹ 1.0? Има ли шанс, поради странността на плаващата запетая, че 1,99999999999... ще даде ›= 1? - person Gray; 03.05.2013
comment
@Gray Трябва да получите странност само ако 1,9999999999999999999 действително е представено като 2. Ако извадите целочислената част от плаваща запетая от плаваща запетая, това не трябва да доведе до закръгляване нагоре. Може да разкрие основната грешка в представянето. Като премахнете целочислената част, вие давате на резултата по-голяма точност, отколкото е необходимо, за да представи стойността. Можете ли да дадете пример за стойност, която придава странност? - person Peter Lawrey; 04.05.2013
comment
Не, не мога @Peter. Просто пишех някакъв защитен код. Правя толкова малко с числа с плаваща запетая (за щастие) и просто се чудех. Обяснението ти има смисъл. Благодаря. - person Gray; 04.05.2013

Мисля, че това би бил най-простият начин:

float n = 22.65f;
float x = n - (int) n;
person VinceStyling    schedule 29.07.2014
comment
Това ще доведе до грешки при закръгляване, опитайте: float a = 22.3f; a - (int)(a) Резултатът ще бъде 0,29999924 вместо 0,3 - person Prateek Batla; 15.06.2016

Малко дълго, но работи:

BigDecimal.valueOf(2.65d).divideAndRemainder(BigDecimal.ONE)[1].floatValue()
person rodion    schedule 16.02.2011
comment
може ли някой да помогне, не ми стана ясно веднага). integerPart - BigDecimal.valueOf(weightInLB).divideAndRemainder(BigDecimal.ONE)[0].toInt() - person Evansgelist; 25.03.2021

Тъй като не всички рационални числа могат да бъдат представени като число с плаваща запетая и 0.6499996... е най-близкото приближение за 0.65.

Например, опитайте да отпечатате първите 20 цифри от числото 0.65:

 System.out.printf("%.20f\n", 0.65f);

->

 0.64999997615814210000

редактиране
Грешките при закръгляването, които се натрупват по време на изчисленията, също играят роля в това, както отбелязаха други.

person Nikita Rybak    schedule 16.02.2011

Ако просто искате да отпечатате числото в 2dp, можете да използвате DecimalFormat.

DecimalFormat df= new DecimalFormat("#.##");
System.out.println(df.format(f));

Ако искате вътрешно числа с фиксирана точка, използвайте BigDecimal

person ashbyp    schedule 16.02.2011

Опитайте тази. Ако таймерът е 10,65, тогава h завършва като първите два знака след десетичната запетая * 100 = 65.

Това е бърз и лесен начин да разделите това, което искате, без проблеми със закръгляването.

float h = (int)((timer % 1) * 100);
person Smorgon    schedule 10.09.2014
comment
@SvenRojek Не използвайте форматиране на код за текст, който не е код. - person user207421; 31.12.2016
comment
За 50.01 понякога дава 0, поради използването на float тип данни. - person Sudhanshu Gaur; 24.02.2018

Кратък отговор: Не можете да представите някои числа точно в двоична система, които са "точни" в десетична система.

Дълъг отговор: http://www-users.math.umd.edu/~jkolesar/mait613/floating_point_math.pdf

[Редактиране]

Също интересно четиво: http://www.cs.berkeley.edu/~wkahan/JAVAhurt.pdf

person Landei    schedule 16.02.2011
comment
Добавих още един интересен :) - person Landei; 16.02.2011

Добрият и перфектен начин за получаване на десетична част от типове данни float и double се използва с String като този код:

float num=2.35f;
String s= new Float(num).toString();
String p=s.substring(s.indexOf('.')+1,s.length());
int decimal=Integer.parseInt(p);
person Gholamali-Irani    schedule 18.08.2016

Опитайте java.math.BigDecimal.

person Bozho    schedule 16.02.2011

Този код ще работи за произволен брой десетични цифри.

float f = 2.3445f;
String s = Float.toString(f);
char[] c = s.toCharArray();
int length = s.length();
int flag = 0;
StringBuilder n = new StringBuilder();
for(int i = 0; i < length; i++)
{
    if(flag == 1)
    {
        n.append(c[i]);
    }
    if(c[i] == '.')
    {
        flag = 1;
    }
}
String result = n.toString();
int answer = Integer.parseInt(result);
System.out.println(answer);
person Anand Kumar    schedule 31.12.2016