Calculation in Arduino not working out - Page 1
Forum section: electronics / general

author: CM800
author_rank: Frequent Contributor
timestamp: May 13, 2016, 11:32:48 am
content: Hi all,I'm trying to attempt a caulcation with some rather nasty numbers, see my code below:Code:float a = 1e6f; // in counts per millisecond squaredfloat t = 16000.0f; // in millisecondsfloat d = 200000.0f; //in countsfloat tr = micros();float velocity = 0.25f * ( (a*t) - sqrt(a) * sqrt((a*t*t) - (8.0f*d)) );float td = micros() - tr;Serial.print(velocity, DEC);Serial.print("c/ms, time: ");Serial.println(td,DEC);The equation is basically to calculate the velocity you need to go at with a given distance, time and acceleration to move from one point to another in a trapazodial motion.I will likely be using much larger numbers in the 'd' (distance) value too.The problem I am getting is that I just simply get 0.000000 as an output.I should be getting:http://puu.sh/oQhTR/100d522819.png12.5 counts/msCould anyone please give me some urgent advice? (I need to have this done by the day is over for a demo for a customer and I'm pulling my hair out on what to do!)Huge thanks in advance.

author: JPortici
author_rank: Super Contributor
timestamp: May 13, 2016, 11:39:34 am
content: "divide and conquer"don't write a big ass equation if you don't know how it will be actually coded/optimizeddo it step by step and see where the problem(s) is (are)

author: Andy Watson
author_rank: Super Contributor
timestamp: May 13, 2016, 11:45:27 am
content: Does micros() return a float? If not you may have to cast it. Ditto sqrt().

author: botcrusher
author_rank: Regular Contributor
timestamp: May 13, 2016, 11:52:56 am
content: You may have to use a a lower precision to et away with it on arduino.The following is from the arduino wiki:QuoteAlso:QuoteYou might have to separate whole numbers from the decimals, do math on exactly what is needed, and then merge them at the final result.The only way around this i van think of is boiling down the equations to the simplest numbers possible and then multiply up, or use string logic to reatach the decimal to a whole number. That is some pretty hefty math for that little arduino, as this is only a snippet I'm guessing at causes here.That 0.000000 is awfully suspicious, I'm assuming that it is returning a null value out of an error. I'd double check you aren't feeding ints or strs to the float (serial input?) If nothing else, the float just might have overflowed.

author: CM800
author_rank: Frequent Contributor
timestamp: May 13, 2016, 11:57:17 am
content: Quote from: Andy Watson on May 13, 2016, 11:45:27 amDoesn't seem to be an issue, pinned it down to this:(sqrt(a) * sqrt((a*t*t) - (8.0f*d))) gives the right answerhowever when I subtract it from (a*t) I get 0 in return for some reason when it should be '75'Code at this second:Code:void loop() {// put your main code here, to run repeatedly:float a = 1e6f;float t = 16000.0f;float d = 300000.0f;float tr = micros();double partOne = ((a*t) - (sqrt(a) * sqrt((a*t*t) - (8.0f*d))));//float velocity = 0.25f * ( (a*t) - sqrt(a) * sqrt((a*t*t) - (8.0f*d)) );float td = micros() - tr;Serial.print(partOne);Serial.print("c/ms, time: ");Serial.println(td,DEC);delay (10);}return image:http://puu.sh/oQiNX/3cfc25438e.png

author: borjam
author_rank: Unknown
timestamp: May 13, 2016, 12:02:52 pm
content: Quote from: TCWilliamson on May 13, 2016, 11:57:17 amPay attention to the number of digits. The maximum number of significant digits you have available are? 7 it seems.a*t are 11 digits.Check the number of digits (ie, decimal logarithm) of the result equation. If the difference in number of digits is larger than 2, you don't have enough precision for that calculation. Are there double precision numbers in Arduino?

author: CM800
author_rank: Frequent Contributor
timestamp: May 13, 2016, 12:16:15 pm
content: Quote from: borjam on May 13, 2016, 12:02:52 pmhmm, I see what you mean. What would be the best way to resolve this?

author: AndreasF
author_rank: Frequent Contributor
timestamp: May 13, 2016, 12:16:59 pm
content: OOOPS, ignore this - got the parentheses put in the wrong place.Your formula is overly complicated and can be heavily simplified:sqrt(a)*sqrt(a*t*t) = sqrt(a)*sqrt(a)*sqrt(t)*sqrt(t) = a*t (assuming both a and t are always positive of course)therefore, your whole formulavelocity = 0.25*(a*t - a*t - 8*d)simplifies tovelocity = -2*dSomehow I doubt that's what you're after.

author: Andy Watson
author_rank: Super Contributor
timestamp: May 13, 2016, 01:20:55 pm
content: Quote from: TCWilliamson on May 13, 2016, 11:57:17 amWould it hurt to go belt and braces and tryfloat tr = (float) micros();Similarly, I believe sqrt() expects doubles and returns a double, use sqrtf to return a float.

author: dannyf
author_rank: Super Contributor
timestamp: May 13, 2016, 01:22:48 pm
content: float types have an effective precision of 5 - 6 digits so the part in the bracket will return 0 or even negative.go with double or revise your algorithm: for example, take sqrt(a*t) out of that bracket and make your calculation around that number. it will greatly reduce the number of calculations.

author: Kalvin
author_rank: Super Contributor
timestamp: May 13, 2016, 02:03:42 pm
content: Welcome to the real world of finite precision.

author: dmills
author_rank: Super Contributor
timestamp: May 13, 2016, 02:38:06 pm
content: I would be very tempted to write this in fixed point, maybe use 64 bit ints and work with something like 56.8 or so (Actually, my first instinct would be to rethink how I was doing whatever it was I was doing).The inputs differ by six orders of magnitude (And the at^2 product is going to be in the 10^16 region), which makes for an interesting precision requirement as loss of precision in the big terms will possibly swamp the much smaller (8.0f * d) term.Floating point is horrible for this sort of thing, especially in single precision.Goldburg wrote the classic paper on this stuff "What Every Computer Scientist Should Know About Floating-Point Arithmetic", you can find a copy here :https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.htmlRegards, Dan.

author: RandallMcRee
author_rank: Frequent Contributor
timestamp: May 13, 2016, 03:14:52 pm
content: Several problems here.Great advice on keeping things simple. Make intermediate results and check them.Find out how to use Double and Float (hopefully before its a crisis; and with programming there is always a crisis).Whenever I see code that uses floating point and I don't see NAN--well there is always a problem. Pretty much any code that needs to work in a non-hobby environment needs to check validity of inputs and outputs using NAN (look it up).Time is often an issue and sure enough you have: "float t1=micros(); float td=micros()-t1;" So, here, td can be either zero or negative (on some platforms, maybe not yours). So any division by td is now suspect (NAN!).Just took another look at micros() definition; it's EVIL!. "This number will overflow (go back to zero), after approximately 70 minutes." So, yeah, that's a totally unreliable number--what if you catch it at a transition. OMG. Useless.To other posters; advice to use Double instead of Float is usually correct but depends on the platform--on some arduinos they map to the same datatype, on others they are different. So another rule, is to know what the underlying datatype actually is, e.g. 4 or 8 byte float/double (if in doubt check!).Good luck!

author: westfw
author_rank: Super Contributor
timestamp: May 13, 2016, 03:50:40 pm
content: QuoteI don't think that "(, DEC}" is a correct print modifier for a floating point number in arduino, if the number is a float.Try just "Serial.println(td);"

author: dannyf
author_rank: Super Contributor
timestamp: May 13, 2016, 04:06:33 pm
content: the closest I could get is 12.499999, without resorting to double types, no sqrt(), 200+ bytes smaller and 25% faster.

author: dannyf
author_rank: Super Contributor
timestamp: May 13, 2016, 04:13:57 pm
content: QuoteFor those particular numbers, I could get it 50% faster (100% faster?): 6000 cycles under my approach vs. 13000 cycles using the standard approach.

author: macboy
author_rank: Super Contributor
timestamp: May 13, 2016, 04:16:12 pm
content: Quote from: borjam on May 13, 2016, 12:02:52 pmActually a*t gives 1.6e10, which is only 2 significant (decimal) digits, not 11. But consider that the exponent is very high. With single precision float, you can't add or subtract a small number such as 75 to/from this (or, equally, obtain a very small result such as 75) because there is no resolution left down at single-digits. You could try double precision floats. Just change all "float" to "double" and your "f" suffix for literals, into a "L" for long double (do not forget to use a decimal point to ensure it will be a long double and not just a long int!).Even better: modify your calculations so that you don't take a difference between to numbers with many orders of magnitude different size. That's never a good idea even with double.Quote from: dannyf on May 13, 2016, 04:06:33 pmDid you come to brag or to help?

author: dannyf
author_rank: Super Contributor
timestamp: May 13, 2016, 04:28:46 pm
content: Quotethe use of double can be problematic, particularly on a small mcu here, as it usually bloats up your code, and your compiler may or may not support it. On my compiler, the use of double types adds 3K byte to my code.

author: Kalvin
author_rank: Super Contributor
timestamp: May 13, 2016, 04:55:18 pm
content: There is at least one library for big numbers implemented for AVR. If you have cycles to burn, the big numbers maybe something to look at. There might be some other libraries available too.

author: CM800
author_rank: Frequent Contributor
timestamp: May 13, 2016, 05:41:58 pm
content: I have been forced down another road due to time constraints and have had to implement the job in an existing product rather then a customized one, needless to say I still learned a lot and I think this will be very useful to know in an upcoming project!

author: helius
author_rank: Super Contributor
timestamp: May 13, 2016, 05:56:03 pm
content: Code:$ bcbc 1.06Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.This is free software with ABSOLUTELY NO WARRANTY.For details type `warranty'.a=1000000t=16000d=200000.25*(a*t - sqrt(a)*sqrt(a*t^2 - 8*d))250.00If you evaluate each part of the formula you can see why this is probably not correct. \$a \cdot t^2 \$ is 256 trillion. \$8 \cdot d \$ is 1.6 million. Differences like this are bad news from a numeric stability perspective because since the minuend is so many orders of magnitude larger than the subtrahend, it only perturbs it by a little tiny bit. The last root will be extremely close to 16 million: 16 million - .05. That precision requires 9 decimal digits to represent, which is about equal to 30 binary digits: the mantissa of a single float only represents 23 bits. You need double floats to represent this quantity accurately.What happens next makes it worse: the result is multiplied by a thousand, which makes it 16 billion - 50, and then it is subtracted from 16 billion. So from an orders of magnitude analysis, we start out with trillions and then cancel them out to zero.This kind of formula is known as numerically unstable. There are worse examples, which don't even work with double floats, but you should re-organize the calculation so you aren't subtracting vast quantities that only slightly differ.

author: dannyf
author_rank: Super Contributor
timestamp: May 13, 2016, 07:30:29 pm
content: The answer is actually quite simple from a numerical analysis perspective, and hints were dropped earlier in the thread as well, some in your own answer.Basically, it is about ways to calculate 1 - sqrt(1 - x) efficiently.The solutions range from high school math from calculus 101.the high school math approach would be to multiply it by 1 + sqrt(1-x) so you have 1 - sqrt(1-x) = x / (1 + sqrt(1 - x)) = ~x / 2: as others have pointed out, since x is so small for those numbers suggested, the denominator degenerates to 2.the calculus approach is to use taylor expansion on sqrt(1-x): 1 - sqrt(1-x) = 1 - (1 - 0.5x - 1/8 x^2 ....) = 0.5x + 1/8 x^2 ......Both will get you to the same place.

author: andyturk
author_rank: Frequent Contributor
timestamp: May 13, 2016, 08:20:23 pm
content: Quote from: TCWilliamson on May 13, 2016, 11:32:48 amThe reason you're getting 0 for the value of 'td' is that the quantity you're printing out should almost always be zero. Paraphrased, your code does this:Code:float timestamp0 = micros();// evaluate an expression in little or no compute timefloat timestamp1 = micros();println("elapsed time = %f\n", timestamp1 - timestamp0);Your code grabs two timestamps with little or no actual time between the calls. So the difference between those two readings will most likely be zero.

author: CM800
author_rank: Frequent Contributor
timestamp: May 13, 2016, 09:15:21 pm
content: Quote from: andyturk on May 13, 2016, 08:20:23 pmNo, that was fine, I was just measuring for performance reasons. The one that I cared about is the OTHER value, regarding the velocity output being zero.

author: westfw
author_rank: Super Contributor
timestamp: May 14, 2016, 01:21:15 am
content: OK. Nothing to do with Serial.print() after all (I actually ran the code...)From your reference calculation, which I should have looked at earlier:1e6*(16e3)2=2560000000000008 * 2e5 = 16000001e6*(16e3)2- 8 * 2e5 = 255999998400000that result differs from the pre-subtraction value at about the 9th digit, and AVR floating point only has six digits of precision. As far as the AVR math is concerned, 1.6e6 is zero, compared to the 256e12 it is being subtracted from.(This is the same thing most people have been saying, only with the actual numbers filled in.)

author: CatalinaWOW
author_rank: Super Contributor
timestamp: May 14, 2016, 04:02:59 am
content: Since you are likely to have predetermined acceleration values and probably limits on maximum velocity this may be an opportunity to completely rethink the approach, using a standard acceleration and decelleration profile. Then you can pre-calculate the distance spent accelerating and decellerating, and all you ask the Arduino to do is calculate the time to go at constant speed to achieve the total motion desired. No unstable math required.

author: IanB
author_rank: Super Contributor
timestamp: May 14, 2016, 04:12:00 am
content: You should use mathematics to solve this problem.(Hint: examine the structure of the expression, simplify it, and use a Taylor series expansion.)With the numbers you have given, the velocity is going to be given with high precision using the following formula:Code:velocity = d / t; // who'd a thunk it?You should not have any problem doing this in single precision float arithmetic.

author: IanB
author_rank: Super Contributor
timestamp: May 14, 2016, 05:11:33 am
content: Details:

author: helius
author_rank: Super Contributor
timestamp: May 14, 2016, 06:15:37 am
content: Very nice worked example. A couple of other answers are still bothering me:Quote from: Andy Watson on May 13, 2016, 01:20:55 pmThis literally does nothing. Conversion of arithmetic types is always automatic in assignments.QuoteThis is true (about the return type). But it isn't required that double be of any greater precision than float: on small MCUs this could be a compiler option. The compiler can also infer from the destination of the expression (initializer tofloat velocity) that greater precision isn't needed, in some cases (not this one).Quote from: RandallMcRee on May 13, 2016, 03:14:52 pmSilent exceptions are only one possible way to manage exceptional conditions in arithmetic. It is usually better to set the mode register to trap them, and use a SIGFPE handler or equivalent.QuoteYou cannot expect to divide by a time variable in any program when the length of time may be zero units. This is a logic error (which should be found at the flowchart stage), not a coding issue. If your calculation was with integer types then you do not get notice of overflow (NaNs or otherwise): the program is just undefined.QuoteTime counters are defined and intended to be used with unsigned arithmetic, where no such problems occur. If the program isunsigned long tr = micros(); unsigned long td = micros()-tr;, thentdwill always be the period of time between the two statements, and cannot underflow.

author: CM800
author_rank: Frequent Contributor
timestamp: May 14, 2016, 09:08:52 am
content: Quote from: IanB on May 14, 2016, 05:11:33 amHi IanB,That's some really useful information there. I will have to look into these maths tools further, Time to brush up on my algebra!I think it's time to call up my maths tutor and get some lessons going again. This could be really useful with some of the directions I am looking to go.Not sure why the timer keeps getting brought up by so many people. It is not part of the codes functionality, I was just using it for evaluating the performance roughly as I wasn't sure just how long that equation could take to calculate for the Arduino (next to nothing thankfully)

author: dannyf
author_rank: Super Contributor
timestamp: May 14, 2016, 10:42:57 am
content: QuoteThat approach is not overflow safe.

author: IanB
author_rank: Super Contributor
timestamp: May 14, 2016, 04:04:28 pm
content: Quote from: dannyf on May 14, 2016, 10:42:57 amRefer to what helius said two posts above. With unsigned arithmetic it is safe and it gives the right answer.

author: helius
author_rank: Super Contributor
timestamp: May 14, 2016, 04:06:45 pm
content: Quote from: dannyf on May 14, 2016, 10:42:57 amNot the case. See ISO C 6.3.1.8: If one operand is unsigned, and its size is greater than or equal to the other operand, both operands are converted to unsigned and the result is unsigned. Unsigned arithmetic is never subject to overflow, by definition.

author: mav_iqdirect
author_rank: Contributor
timestamp: May 14, 2016, 06:05:53 pm
content: You also may try to use double data type instead of float.

author: bingo600
author_rank: Super Contributor
timestamp: May 14, 2016, 06:16:27 pm
content: Since when did avr-gcc/avr-libc get working double precision libs ?Last i checkked they would treat double like float/Bingo

author: Rick Law
author_rank: Super Contributor
timestamp: May 14, 2016, 09:41:21 pm
content: Quote from: bingo600 on May 14, 2016, 06:16:27 pmThey still do. Arduino library still treats double the same as float and there is NO precision difference. Double and Float are identical 4 byte variables.It is like keeping a super model's photo in your wallet. That isn't going to get you a date with her but you can enjoy the sight.https://www.arduino.cc/en/Reference/Double

author: richardman
author_rank: Frequent Contributor
timestamp: May 17, 2016, 08:29:16 am
content: To summarize the issue with the "precision" issue, with 32-bit floats, you have ~7.3 decimal digits of precision. Why .3? Because human uses decimal notation and computers use binary. What this means is that you can store0.1234567eXwhere X is the power of ten from -38 to 38 accurately. To do arithmetic though, thinking of it that depending on the operations, you have to normalize the power-of-ten to the same or at least close-by to each other. When you do that, you can then see that if the exponents vary too greatly, then the result will not be accurate.