Numbers In App Inventor Are Stored As Floating-Point

I am exploring App Inventor, an Android application development environment for novice programmers. I am teaching it to my kids, as an introductory step towards “real” app development. While playing with it I wondered: are its numbers implemented in decimal? No, they aren’t. They are implemented in double-precision binary floating-point. I put together a few simple block programs to demonstrate this, and to expose the usual floating-point “gotchas”.

App Inventor Blocks To Display 0.1 to 17 Digits
App Inventor Blocks To Display 0.1 to 17 Digits


(Also check out this recent discovery about integers: entered in blocks they are represented in floating-point; generated through calculations they are represented as arbitrary-precision integers.)

App Template

Each testcase I wrote is embedded in a simple app “template”, consisting of a button and a label (I used the default names, Button1 and Label1). The button is used to run the testcase, and the label is used to display its output. I ran each test on both the emulator and a real device (a Samsung Galaxy Tab 3), although I only show screenshots of the emulator in the examples below.

The main tool I used is a math block called “format as decimal”:

“format as decimal” block
“format as decimal” block

The ‘places’ parameter specifies how many digits to print after the decimal point. I discovered it can print a number to any number of decimal places; this was a surprise, given that many industrial strength programming language implementations can’t do that! This allowed me to show the floating-point implementation in all its glory.

Printing Enough Digits To Expose Floating-Point Implementation

The following program (the same one shown in the introduction) prints the number 0.1 to 17 digits:

App Inventor Blocks To Display 0.1 to 17 Digits
App Inventor Blocks To Display 0.1 to 17 Digits

Here is its output:

Emulator Output Showing 0.1 to 17 Digits
Emulator Output Showing 0.1 to 17 Digits

You can see the “extra 1 on the end”, the tell-tale sign of a binary floating-point representation; let’s verify by printing all its digits:

Blocks To Display All 55 Digits of 0.1
Blocks To Display All 55 Digits of 0.1

Those blocks in fact display the exact representation of 0.1 in double-precision floating-point:

Output Showing All Digits of 0.1
Emulator Output Showing All Digits of 0.1

Numbers Are Converted Before They’re Displayed

There’s a simpler way to show that App Inventor uses floating-point, or at least get a clue that that’s what it’s using. If you enter a number of 16 to 17 digits or more, it will be changed as soon as you hit enter. It is converted instantly, like in a spreadsheet cell.

In this example, I entered 0.12345678901234567, even though App Inventor displays it as 0.12345678901234566 in the number block:

0.12345678901234567 Entered And Converted Instantly to 0.12345678901234566
0.12345678901234567 Entered And Converted Instantly to 0.12345678901234566

I wrapped the number in a “format as decimal” so that I could show all its floating-point digits:

Output Showing All Digits of 0.12345678901234567
Output Showing All Digits of 0.12345678901234567

That number, 0.1234567890123456634920984242853592149913311004638671875, is in fact the double-precision floating-point number closest to 0.12345678901234567. App Inventor displays it as 0.12345678901234566 because that is the full floating-point number rounded to 17 digits. So even though App Inventor changed the external representation from 0.12345678901234567 to 0.12345678901234566, it doesn’t matter; both convert to the same floating-point number internally.

Floating-Point Gotchas

The three examples that follow show mathematical results that surprise the uninitiated; I’ve written about them in other contexts.

Equality is Broken

Simple decimal addition fails to work in floating-point; for example, 0.3 + 0.6 ≠ 0.9. Here are blocks to demonstrate that:

Blocks That Show 0.3 + 0.6 Does Not Equal 0.9
Blocks That Show 0.3 + 0.6 ≠ 0.9

(Notice the ‘\n’ formatting — a shout out to C!)

This exposes two issues:

  • 0.3 and 0.6 converted to floating-point, and then added, does not give 0.9.
  • 0.9 converted to floating-point does not even give 0.9!
Output Showing Result of 0.3 + 0.6
Output Showing Result of 0.3 + 0.6

The Floor Function Is Broken

4.35 * 100 is 435, so floor(4.35 * 100) is 435, right? Not in floating-point. 4.35 * 100 is slightly less than 435, so the floor is 434.

Blocks That Show floor(4.35 * 100) Does Not Equal 435
Blocks That Show floor(4.35 * 100) ≠ 435
Output Showing Result of floor(4.35 * 100)
Output Showing Result of floor(4.35 * 100)

Let’s look at all the digits to verify:

Blocks That Show 4.35 * 100 Does Not Equal 435
Blocks That Show 4.35 * 100 ≠ 435
Output Showing Result of 4.35 * 100
Output Showing Result of 4.35 * 100

Algebra Is Broken

I wrote this C function to show that floating-point does not honor algebraic manipulations:

double f(double a)
{
 double b, c;

 b = 10*a - 10;
 c = a - 0.1*b;

 return (c);
}

Algebraically, it should always return 1: c = a – 0.1*(10*a – 10) = a – (a-1) = 1.

I rewrote that function in blocks, and I tested it on input 719525522284533115.3 (which App Inventor converted instantly to 719525522284533100, a clue that this is not going to go as expected):

Blocks To Show Function Result Is Not 1
Blocks To Show Function Result Is Not 1

The answer is not 1, but -128!

Output Showing Answer is -128, not 1
Output Showing Answer is -128, not 1

Should App Inventor Hide Binary?

App Inventor hides much of the complexity of Android app development, allowing non-programmers to easily write simple apps. Yet it doesn’t hide binary floating-point, something that trips up countless people, beginners and non-beginners alike. Should it? Arbitrary-precision decimal floating-point would fix the most glaring gotchas, while rational arithmetic would handle expressions involving fractions like 1/3. (Rational arithmetic might be overkill though, since anyone that has used a calculator would expect inexact results using decimal fractions.)

Maybe it’s best to hide binary until new programmers get more experience — and start programming real apps in Java.

Dingbat

8 comments

  1. I’ve got a unit conversion program that was giving me weird answers for 0C to F and R. and sometimes for other problems. I jury-rigged a work-around but I know all I did was hid the problem.

    So THAT’S what’s going on. What can we do about it? Can I just show the results of calculation (since it’s big intigers) as decimals and just eliminate the whole thing?

  2. Well, for each unit, there’s two conversion fractions (two denominators and two numerators each) and for the temperatures there’s also subtraction and addition. Some of the parts of the fractions are in decimal form.

    Tomorrow, I’ll give you a specific example.

  3. So here’s an example, m to ft.

    m*(100/1)*(1/2.54)*(1/12)=ft.

    As you can see, part of this is in decimal form. I can think of three solutions.

    1. Use text blocks rather than number blocks. Math functions could simply reject them.
    2. Convert to decimal at specific points, but that would still leave decimals in text blocks to be automatically converted to bicimals.
    3. turn 1/2.54 to 100/254, but there would be a LOT of code to get through to fully implement. So, of course, this is the most likely to work.

  4. @Frank,

    I think you have to go with something along the lines of solution #3.; that is, use integers. Floating-point literals in text blocks are still converted to IEEE (i.e., limited-precision) floating-point.

  5. I know that this isn’t what this site is about, but it’s too cool to not share.

    I made Android give me a complex number as an answer. I’d done it before, but this time was cooler than the others.

    In my app’s probability conversion units I have logarithmic units of probability and in the upcoming version, I have /π as a unit (that is, per π as % is per hundred) as a unit. And I was playing around, converting various things from /π to various logarithmic units and I discovered that anything greater than π/π is not only ridiculous (as that would be greater than 1), but also crates a complex number when converted to one of the logarithmic units of probability. I ALSO discovered that when converted to nats (which is a logit based on e) it the complex number has the form x+ πi.

    Which got me excited.

    So I set finding out what probability gets you 1+ πi. It turns out that 4.96992639477188 /π probability (or just 1.58197671 which I know is more than certainty) equals 1+ πi nats. Which I think is REALLY cool.

Comments are closed.

Copyright © 2008-2024 Exploring Binary

Privacy policy

Powered by WordPress

css.php