Java float is more precise than double?
Asked Answered
S

4

8

Code:

class Main {
    public static void main (String[] args) {
        System.out.print("float: ");
        System.out.println(1.35f-0.00026f);
        System.out.print("double: ");
        System.out.println(1.35-0.00026);
    }
}

Output:

float: 1.34974
double: 1.3497400000000002

??? float got the right answer, but double is adding extra stuff from no where, Why??

Isn't double supposed to be more precise than float?

Sterner answered 18/10, 2013 at 7:34 Comment(1)
try this System.out.println(String.format("%.20f", 1.35f-0.00026f));Bobine
D
12

A float is 4 bytes wide, whereas a double is 8 bytes wide.

Check What Every Computer Scientist Should Know About Floating-Point Arithmetic

Surely the double has more precision so it has slightly less rounding error.

Squeezing infinitely many real numbers into a finite number of bits requires an approximate representation. Although there are infinitely many integers, in most programs the result of integer computations can be stored in 32 bits. In contrast, given any fixed number of bits, most calculations with real numbers will produce quantities that cannot be exactly represented using that many bits. Therefore the result of a floating-point calculation must often be rounded in order to fit back into its finite representation. This rounding error is the characteristic feature of floating-point computation.

On a side note:-

I would suggest if you want the exact decimal values then use java.math.BigDecimal

Desperation answered 18/10, 2013 at 7:36 Comment(0)
S
3

The reason of this is the mechanism of numbers with floating point calculation. Also when java tries to print a float value, it truncates trailing zeroes. And, you know, double type uses 8 bytes and float uses 4. So the double precision is bigger.

So, when java calculates your float value, it gets something like 1.3497400 and truncates it before output. And when java calculates your double value, it gets more digits and so you get such result.

Try to execute this simple example for better understanding:

public class Test {
    public static void main( String[] args ) {
        float sum = 0;
        for (int i = 1; i <= 10; i++) {
            sum += 0.1;
        }
        System.out.println( sum );
    }
}

public class Test {
    public static void main( String[] args ) {
        double sum = 0;
        for (int i = 1; i <= 10; i++) {
            sum += 0.1;
        }
        System.out.println( sum );
    }
}
Silkworm answered 18/10, 2013 at 7:57 Comment(0)
A
2

If you know something about the rules for converting double values to strings, which are specified by the documentation for Double.toString [Java-API], you know that the program prints the shortest decimal fraction sufficient to distinguish the double value from its nearest neighbor, with at least one digit before and after the decimal point.

Check Joshua Bloch's Java Puzzlers: Traps, Pitfalls, and Corner Cases - Puzzle 2: Time for a Change. He explains this question in that chapter.

Angulo answered 18/10, 2013 at 7:52 Comment(0)
N
1

Perversely, you see what you're seeing because double is more precise than float, rather than in spite of it. What you're seeing is a result of how floating point numbers are converted to strings by the default toString operation of each class, which depends in part on the precision of the format of the floating point number.

TL;DR: The default "to string" operations for float and double each create strings that only contain as many digits as necessary to uniquely identify the value in the float or double relative to the adjacent representable values in that same format, rather than outputting all digits of the value. Since double can represent more values than float, its string has to be more precise.

Details:

Let's start here: The actual value you get from 1.35f - 0.00026f isn't 1.34974, it's 1.34974002838134765625 (since, as you probably know, not all floating point numbers can be represented in either the float or the double format). The double equivalent 1.35 - 0.00026 gives you a more accurate value: 1.349740000000000161861635206150822341442108154296875. (I used baseconvert.com's IEEE-754 converter for these, double-checking the 32-bit results against h-schmidt.net and by using the code below.) Let's see that by using BigDecimal to format those values to strings rather than toString (live copy):

import java.math.BigDecimal;

public class Example {
    private static String format(float value) {
        return new BigDecimal(value).toPlainString();
    }
    private static String format(double value) {
        return new BigDecimal(value).toPlainString();
    }
    public static void main(String[] args) throws Exception {
        float  f = 1.35f - 0.00026f;
        double d = 1.35  - 0.00026;

        System.out.println("Float:  " + format(f));
        // "Float:  1.34974002838134765625"

        System.out.println("Double: " + format(d));
        // "Double: 1.349740000000000161861635206150822341442108154296875"
    }
}

In fact, neither 1.35 nor 0.00026 is precisely held by either float or double either, so the imprecision kicks in even before the subtraction.

Okay, so why is float's toString giving you just "1.34974" when double's toString gives you "1.3497400000000002"? Because the toString methods of both classes (float, double) produce the shortest (usually) output for a value that differentiates it from the representable values adjacent to it on either side.

With float, because float is quite imprecise, the short string "1.34974" is enough to identify the value the float actually contains (and not one of the values next to it). That is, even though the actual value is 1.34974002838134765625, not all of those digits are needed to differentiate it from other representable float values, all you need in the realm of float is "1.34974". Float.parseFloat("1.34974") gives you the float value 1.34974002838134765625 again.

Not so for double, though, because double has more representable values than float does, so the string "1.34974" doesn't uniquely identify the double value 1.349740000000000161861635206150822341442108154296875. In fact, if you parse "1.34974" as a double, you get 1.3497399999999999398170302811195142567157745361328125 instead. So it has to include more digits to differentiate the double value you're making into a string from other double values — because double is more precise than float.

Nut answered 27/12, 2023 at 12:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.