Processing 4 Java Bug bad printing of a number

Have got a bug - look like its JAVA that freaking.

float [][] vDATA = {{45576469, 84.847, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95, },     //  tous  253
{45576469, 84.861, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 87, 88, 89, 91, 92, 93, 94, 95, 96, -86, },     //  tous  254
{45576469, 84.875, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 87, 88, 89, 91, 92, 93, 94, 95, 96, -86, },     //  tous  255
{45576469, 84.889, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95, },     //  tous  256
{45576469, 84.903, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95, }} ;     //  tous  257

for(int i = 0; i< vDATA.length ; i++)  {
  
  println( (int)vDATA[i][0] + ", " +  vDATA[i][1] + ", ") ;   

  
  
  
  
}
/*
Result   
45576468, 84.847,          // should be 45576469 ????
45576468, 84.861, 
45576468, 84.875, 
45576468, 84.889, 
45576468, 84.903, 
*/
1 Like

Yes but why

printing 45576469 , 84.847, give 45576468 , 84.847,

Ok Find the solution but I don’t know why Java react like that :slight_smile:

float [][] vDATA = {{ 45576469 , 84.847, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95 },     //  tous  253
{ 45576469 , 84.861, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 87, 88, 89, 91, 92, 93, 94, 95, 96, -86 },     //  tous  254
{ 45576469 , 84.875, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 87, 88, 89, 91, 92, 93, 94, 95, 96, -86 },     //  tous  255
{ 45576469 , 84.889, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95 },     //  tous  256
{ 45576469 , 84.903, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95 }} ;     //  tous  257

for(int i = 0; i< vDATA.length ; i++)  {
  
  println(  (int)vDATA[i][0] + ", " +  vDATA[i][1] + ", ") ;   

  

}

/*
45576468, 84.847,     BUG 45576468  --> 69
45576468, 84.861, 
45576468, 84.875, 
45576468, 84.889, 
45576468, 84.903, 
*/

println() ;

int [][] vDATA2 = {{ 45576469 , 84847, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95 },     //  tous  253
{ 45576469 , 84861, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 87, 88, 89, 91, 92, 93, 94, 95, 96, -86 },     //  tous  254
{ 45576469 , 84875, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 87, 88, 89, 91, 92, 93, 94, 95, 96, -86 },     //  tous  255
{ 45576469 , 84889, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95 },     //  tous  256
{ 45576469 , 84903, 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95 }} ;     //  tous  257

for(int i = 0; i< vDATA2.length ; i++)  {
  
  println(  vDATA2[i][0] + ", " +  vDATA2[i][1]/1000.0  + ", ") ;   

  

}
/*
45576469, 84.847,     // Good print
45576469, 84.861, 
45576469, 84.875, 
45576469, 84.889, 
45576469, 84.903,
*/

Floating-point numbers are stored in a format similar to numbers in scientific notation with an exponent and a mantissa (the significant digits) all packed into either 32 bits of space for a float or 64 bits for a double.

For a float data type, the mantissa stores 23 bits with an implied leading 1 as a 24th bit. That means a float can only store integer values up to 2^24 or 16777216 before they start losing precision. The other 9 bits in the float store the sign and 8 bits for the exponent.

If you try to assign an integer with a value larger than 2^24 into a float, the lowest binary digits simply won’t be stored, effectively be zero-ing them out. Printing isn’t the problem. Storing a number with enough precision is the problem.

A double stores 53 bits in its mantissa, so it can easily store all the values of a 32-bit integer without loss. Or, as you do, you can store the numbers as integers as long as they are smaller than 2^31 or 2147483648 (leaving the 32nd bit for the sign).

2 Likes

Given the index[1] is the only float primitive datatype, you can store it separately:
final float[] fDATA = { 84.847, 84.861, 84.875, 84.889, 84.903 };

Also, the index[0] is the only big int value. For the other indices, a byte is just enough:

final int[] iDATA = { 45576469, 45576469, 45576469, 45576469, 45576469 };

final float[] fDATA = { 84.847, 84.861, 84.875, 84.889, 84.903 };

final byte[][] vDATA = {
  { 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95 },      // tous 253
  { 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 87, 88, 89, 91, 92, 93, 94, 95, 96, -86 }, // tous 254
  { 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 87, 88, 89, 91, 92, 93, 94, 95, 96, -86 }, // tous 255
  { 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95 },      // tous 256
  { 45, 57, 64, 69, 73, 76, 79, 81, 83, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95 }       // tous 257
};

for (int i = 0; i < vDATA.length; i++)
  println(i + ":", iDATA[i] + ",", fDATA[i] + ",", join(str(vDATA[i]), ", "));

IEEE 754 floating point representation is a way of storing an approximation of a floating point value.
Plug the numbers in at: IEEE-754 Floating Point Converter

The error is 2.1941146866818489163783179429718e-8 : don’t use floating point when you want integers.

1 Like

Hello @Max_Normal ,

Please provided details to reproduce this.

Thanks!

:)

Plug 45576469 into the floating point converter, you’ll see that it is stored as 45576468 – an error of 1. 1/45576469 = … 2.194e-8

double will work but the Processing procedures need a cast to float so using nf(…) to tidy the output means that the value will still be off by one. Using Java string formatting (implicitly imported) gets round this…

double foo = 45576469;
float bar = 45576469;

String stringyNum = String.format(“%.0f”, foo);
println("Java formatted double: " + stringyNum);
println("nf(…) cast to float: " + nf((float)foo));

stringyNum = String.format(“%.0f”, bar);
println("Java formatted float: " + stringyNum);

Java formatted double: 45576469
nf(…) cast to float: 45576468
Java formatted float: 45576468

1 Like

Make sure to suffix every double literal w/ a d or D inside a “.pde” file:
final double foo = 45_576_469d;

Makes no difference here, the underlying issue is that IEEE-754 stores approximate values and in most cases the errors are (cough) within acceptable bounds. Big integers don’t play well with floating point formats.

I’m not so sure that the ‘d’ suffix is that necessary for a declare and assign statement, double foo = 45576469; and double foo = 45576469d; are not equivalent but they are functionally similar, in the former call the base number will be treated as an integer and converted implicitly to a double.

Little things like this are why we test everything that we write :slight_smile:

1 Like

Indeed, for an int literal, the d suffix is totally unneeded.

But for a literal, intended to be a double, includes a dot . or an e, we have to suffix it w/ a d inside a “.pde” file, so the Processing IDE (PDE)'s pre-processor won’t suffix it w/ an f!

1 Like

I understand your point but wouldn’t a floating point value be range checked at compile time and cast upwards if necessary? Try typing float foo = 3e96; Parser catches badness, caught before compile time and the compiler will be at least as smart.

One of the beauties of Processing is hmmm, unsure about this – start IDE, simple sketch, rattle off a few test cases – shiny, back to main task.

Processing’s “Java Mode” has a pre-processor that acts upon all files w/ the “.pde” extension.

If a literal containing any of the characters “.”, “e” or “E”, will automatically be suffixed w/ an “f”, unless it’s already suffixed.

2 Likes

Good point…

double foo = 3e66; // not so good, 3e66 read as float, range error flagged
double foo = 3e66d; // dandy! [edit – added semicolon]

Still caught pre-compile [edit – added this line].

Little things like this are why we test everything that we write :slight_smile:

2 Likes