I'm programming a CNC on their own, and I'm in the middle of my code, and now I have two variables of type long that, when divided results in an exact fractional value, but the Arduino returning a fractional value of only 2 numbers after the dot.
long x = 99;
long y = 9;
long z = 9999;
long m = 9999;
Back when I worked with NC/CNC ot was all G-codes with leading zeros, but how about this way of using integers as it's like working in thousandths instead of inches except millionths to get more decimal places.
If I need 3 places, I work to 6 and roundoff doesn't reach my significant digits that do matter.
Also, 32-bit floats (the only kind) on Arduino are over 100x slower than 32-bit longs.
Floats aren't always clean, what should be 1.0 can come up 0.9999999 just from operating on floats.
This working example shows 6 places, but you can see how to change that if desired?
long sixPlaces = 1000000L;
void printSixPlaces( long val )
{
Serial.print( val / sixPlaces );
Serial.print( "." );
val = val % sixPlaces;
if ( val < 100000L ) // the ladder ain't pretty
{
Serial.print( "0" ); // but it's faster than
if ( val < 10000L ) // a loop with a divide in it.
{
Serial.print( "0" );
if ( val < 1000L )
{
Serial.print( "0" );
if ( val < 100L )
{
Serial.print( "0" );
if ( val < 10L )
{
Serial.print( "0" );
}
}
}
}
}
Serial.print( val % sixPlaces );
}
void setup()
{
// put your setup code here, to run once:
Serial.begin( 115200 );
long x = 99;
long y = 9;
long z = 9999;
long m = 9999;
// (double)x/(double)m; //result 0.01
// (double)y/(double)m; //result 0.00
// (double)z/(double)m; //result 1.00
Serial.println( );
Serial.println( "This is what you show humans." );
Serial.println( );
Serial.print( " " );
Serial.print( x );
Serial.print( " / " );
Serial.print( m );
Serial.print( " = " );
printSixPlaces( sixPlaces * x / m );
Serial.println( );
Serial.print( " " );
Serial.print( y );
Serial.print( " / " );
Serial.print( m );
Serial.print( " = " );
printSixPlaces( sixPlaces * y / m );
Serial.println( );
Serial.print( z );
Serial.print( " / " );
Serial.print( m );
Serial.print( " = " );
printSixPlaces( sixPlaces * z / m );
Serial.println( );
Serial.println( );
Serial.println( "These are what your code works with." );
Serial.println( );
Serial.println( sixPlaces * x / m );
Serial.println( sixPlaces * y / m );
Serial.println( sixPlaces * z / m );
}
void loop()
{
// put your main code here, to run repeatedly:
}
For a CNC application, you really don't want to use floating point at all, other than perhaps to generate the displayed value for a DRO. You surely don't want to use them anywhere in the motion calculations, or the rounding errors will accumulate significant errors.
Be careful: the result of a multiplication by 1000000 could easily exceed the limit of a "long" (signed long: 2,147,483,647; unsigned long: 4,294,967,295). If it does, then you will get garbage.
odometer:
Be careful: the result of a multiplication by 1000000 could easily exceed the limit of a "long" (signed long: 2,147,483,647; unsigned long: 4,294,967,295). If it does, then you will get garbage.
If you were me, you'd have to forget what I know about maths first, maths I learned without handheld electronic calculators that only started to get under-$100-cheap during my last year of HS.
You'd have to forget all the business and production code I wrote back before PC's had FPU's. That's over 10 years of getting it right to the penny or other mark for over 10 years.
I can multiply 1 million times 2000 and not overflow a long, so in this case I decided to show 6 places where 3 or 4 would do.
If I wanted to go higher, I'd cast my data as 64-bit long long and do my multiplies and divides there.
In fact with Forth that is how the scaling operator, */, works except it does 16 to 32 bit.
Yeah, you do have to take care that your possible results of any step do not exceed your variables.
Last time I checked, string.h functions required me to keep my strings inside my array bounds too.
If I wanted to go higher, I'd cast my data as 64-bit long long and do my multiplies and divides there.
That chews up a lot of progmem.
By "long division" I meant:
Do integer divide.
Show quotient as next digit of result.
Multiply remainder by 10, and use the result as the new dividend.
Lather, rinse, repeat.
Here is a little C++ program I wrote:
#include <iostream>
using namespace std;
int main() {
// simulation of long division
// numbers to be divided
int32_t top = 1000000;
int32_t bottom = 998; // this not to exceed 200 million!!
// how many decimal places?
int32_t places = 18;
// do the division
int32_t piece;
int32_t remainder;
cout << top << " / " << bottom << " = ";
piece = top / bottom; // integer part of quotient
cout << piece << "."; // output the integer part
remainder = top - (piece * bottom); // remainder for next step
// next we calculate the "decimal" part of the quotient
for (int32_t i = 1; i <= places; i++) {
remainder *= 10; // "bring down" a zero
piece = remainder / bottom; // next digit of quotient
cout << piece; // output that digit
remainder -= (piece * bottom); // new remainder
}
cout << " (to " << places << " decimal places)";
return 0;
}
Arduino version:
// simulation of long division
// numbers to be divided
int32_t top = 1000000;
int32_t bottom = 998; // this not to exceed 200 million!!
// how many decimal places?
int32_t places = 18;
// do the division
int32_t piece;
int32_t remainder;
Serial.print(top, 10);
Serial.print(" / ");
Serial.print(bottom, 10);
Serial.print(" = ");
piece = top / bottom; // integer part of quotient
Serial.print(piece, 10); // output the integer part
remainder = top - (piece * bottom); // remainder for next step
// next we calculate the "decimal" part of the quotient
for (int32_t i = 1; i <= places; i++) {
remainder *= 10; // "bring down" a zero
piece = remainder / bottom; // next digit of quotient
Serial.print(piece,10); // output that digit
remainder -= (piece * bottom); // new remainder
}
Serial.print(" (to ");
Serial.print(places, 10);
Serial.print(" decimal places)");
I think we've been talking past each other. The data is 32-bit but the calculations are performed with 64-bit variables.
With scaling as an example; I want to scale 0-1023 analog reads to 0-4995 millivolts values.
If I multiply the input by 5000 and then divide that by 1024, not divide then multiply.
Integers round down to 1's. If you then multiply you magnify any step effects.
Honest, I learned this from Charles Moore via Forth but it can be tested.
By "long division" I meant:
Do integer divide.
Show quotient as next digit of result.
Multiply remainder by 10, and use the result as the new dividend.
Lather, rinse, repeat.
Calculations can be done to high precision using BCD or manipulating text digit something like that.
It's not going to be super fast but it can be as