How to create lookup table

I always avoid float math on processors without FPU when possible, and even with, I don't like FP.
Cast the intermediate values to 32 bit, it's more than a magnitude faster.

GoForSmoke:
I always avoid float math on processors without FPU when possible, and even with, I don't like FP.
Cast the intermediate values to 32 bit, it's more than a magnitude faster.

very true, but the integer division makes the math less precise as it truncates to 0
straightforward casting to long won't help for the given formula

newVal = 260 - ((260-240) * ((180-162)/(184-162)))

as in the division the numerator > denominator

doing the multiplication first improves the result - and there casting to long will prevent overflow.

newVal = 260 - ((260-240) * (180-162)) /(184-162)

the result is now 244 which is in fact quite good.

robtillaart:

GoForSmoke:
I always avoid float math on processors without FPU when possible, and even with, I don't like FP.
Cast the intermediate values to 32 bit, it's more than a magnitude faster.

very true, but the integer division makes the math less precise as it truncates to 0
straightforward casting to long won't help for the given formula

newVal = 260 - ((260-240) * ((180-162)/(184-162)))

as in the division the numerator > denominator

doing the multiplication first improves the result - and there casting to long will prevent overflow.

newVal = 260 - ((260-240) * (180-162)) /(184-162)

the result is now 244 which is in fact quite good.

Answer is to work in smaller units. If I want meters to 3 places then I work in millimeters or smaller depending on what math I will be doing. 32 bit int gives me 9 places. 64 bit int gives me 19 places. Both are much faster than 32 bit float.

GoForSmoke:
...
Answer is to work in smaller units. If I want meters to 3 places then I work in millimeters or smaller depending on what math I will be doing. 32 bit int gives me 9 places. 64 bit int gives me 19 places. Both are much faster than 32 bit float.

Can be effective but not in this case, the point of my reasoning,

(180-162)/(184-162) => 0 if all numbers are treated as integer

making them long does not change that

(180000-162000)/(184000-162000) => 0L

very true, but the integer division makes the math less precise as it truncates to 0
straightforward casting to long won't help for the given formula

Answer is to work in smaller units.

newVal = 260000 - (( 260000 - 240000 ) * ( 180000 - 162000 ) / ( 184000 - 162000 )) = 243636

void setup( void )
{
  Serial.begin( 115200 );
  Serial.println( );
  
  long range = 260000L - 240000L;
  long scaleMult = 180000L - 162000L; 
  long scaleDiv = 184000L - 162000L; 
  unsigned long newVal = 260000L - ( range * scaleMult / scaleDiv );

  Serial.println( newVal );
  
  Serial.print( newVal / 1000L );
  Serial.print( "." );
  newVal %= 1000L;
  if ( newVal < 100L )  Serial.print( "0" );
  if ( newVal < 10L )   Serial.print( "0" );
  if ( newVal == 0L )   Serial.print( "0" );
  else Serial.println( newVal );
}

void loop( void )
{
}

I used these techniques for industrial machining (NC/CNC), payroll and billing code long before FPU's became standard default PC hardware. You want to see someone go off, let them find out that their paycheck is a penny too low or their bill is a penny too high. :roll_eyes:

robtillaart:

GoForSmoke:
...
Answer is to work in smaller units. If I want meters to 3 places then I work in millimeters or smaller depending on what math I will be doing. 32 bit int gives me 9 places. 64 bit int gives me 19 places. Both are much faster than 32 bit float.

Can be effective but not in this case, the point of my reasoning,

(180-162)/(184-162) => 0 if all numbers are treated as integer

making them long does not change that

(180000-162000)/(184000-162000) => 0L

That's fine but for other values you can get 0 using 16 bit and non-zero using 32 bit.
That is a result of precision.

For this typical algorithm that is not true as the division is to determine the fractional part between two consecutive values in the lookup table. That means the numerator is always smaller than the denominator resulting in 0 in any integer domain.
By doing the multiplication part first you prevent to some extend the truncating division.

But you are right that there are many many algorithms that will have better accuracy when doing the math with more bits.

I see. Pre-scaling won't work that way with integers. But I don't pre-scale.

In Forth there is a scaling operator, */, that takes 3 values, multiplies the 1st 2 then divides by the 3rd.
It promotes the values to double-word (16 bit to 32 bit) and returns a single word, all integers.

When step 1 is to multiply what is to be scaled by the scaler numerator, it's okay if the denominator is bigger.

multiplies the 1st 2 then divides by the 3rd.

that is exactly what I state

By doing the multiplication part first you prevent to some extend the truncating division.

Forth promotes the values to 32 bit to prevent an overflow of the 16 bits multiplication.
(Good old Forth knows its numerics)

1 Like

Hello,

Apologies to dig up and old thread, but I'm dealing with a similar issue, the only difference my readings are both ascending:

float tankCalibration[30] = {
0, 0,
20.0, 151,
21.5, 200,
28.0, 300,
35.0, 400,
41.0, 500,
47.5, 600,
54.0, 700,
60.5, 800,
66.0, 900,
73.0, 1000,
79.5, 1100,
86.5, 1200,
96.5, 1300,
109.0, 1401
};

Does anyone know how I adapt the code here to lookup the right value?

Which code are you looking at? I am working on the exact same issue as you right now.

dlabun:
Which code are you looking at? I am working on the exact same issue as you right now.

Did you not find the replies in this thread useful?