3

In NSURLSession, when my file is downloading I keep track of the progress with:

CGFloat progress = (CGFloat)totalBytesWritten / (CGFloat)totalBytesExpectedToWrite;

To update an on-screen indicator. I need to make sure once the download is complete though that the indicator disappears, as in my case I show the image being downloaded.

Is if (progress == 1.0) { ... } okay as a check? It may seem silly but I want to make sure there's no edge cases I'm failing to consider, as in the past I've been bit by weird division results with programming, maybe it will return 1.000000 or 1.0000001828111 or something, or could it even do something like 0.99999999999999999?

Doug Smith
  • 29,668
  • 57
  • 204
  • 388

2 Answers2

3

Given:

CGFloat progress = (CGFloat)totalBytesWritten / (CGFloat)totalBytesExpectedToWrite;

It's completely reasonable to assume progress == 1.0 if (CGFloat)totalBytesWritten == (CGFloat)totalBytesExpectedToWrite is true. This should in turn be true if totalBytesWritten == totalBytesExpectedToWrite, assuming the compiler is standards-conforming, but that might be a bad assumption. There are ways the compiler could botch this by handling excess precision incorrectly, but I don't believe they apply on your target system. Still, the update ("edit") to Arkku's answer is a great idea: when you compute progress, simply force it to 1.0 if totalBytesWritten == totalBytesExpectedToWrite is true.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • May I ask two questions? Does C, or IEEE 754, guarantee `42 / 42. == 1`? If yes, are there compilers in which this is broken, as suggested by your answer? – Joseph Quinsey Feb 24 '14 at 03:23
  • 2
    IEEE 754 guarantees `x/x==1.0` for any finite `x`. For `42/42.`, you're very safe. The issue arises when you have an integer that's not representable in your floating point type, e.g. `1000000000` in `float` or some 54+ bit number in `double`. If the two integers are equal, they should both get rounded to the same floating point value in the destination type. However, on systems with "excess precision" (mainly x86 with the old 387 fpu as opposed to sse2), it's possible that a broken compiler might not respect the C semantics for converting to the nominal type when a cast is seen... – R.. GitHub STOP HELPING ICE Feb 24 '14 at 04:00
  • 2
    ...and might instead keep excess precision. In this case, if the compiler is also broken with respect to its handling of spilling intermediates, one of the two operands might get rounded down to its nominal precision (e.g. `double`) while the other gets kept internally in excess precision (`long double`). Then you get a non-exact result when the division is performed. Again, **all** of these behaviors would be broken and non-conforming, but GCC on x86 was broken in this regard up until 4.5.x, and still may be broken without `-std=c99` of `-fexcess-precision=standard`. – R.. GitHub STOP HELPING ICE Feb 24 '14 at 04:02
  • 2
    Thanks for the breakdown, @R.., it was really informative and helpful. – Doug Smith Feb 24 '14 at 05:13
2

The safe check using the result of the floating point division would be something like:

if (progress > (1.0f - FLT_EPSILON))

However, assuming the numbers of bytes are integers, you can simply check:

if (totalBytesWritten == totalBytesExpectedToWrite)

edit: If, as per comments, you cannot pass the integers on to wherever you need to do this check, you could also ensure that progress equals 1.0f in that case:

progress = (written < expected) ? ((CGFloat)written / expected) : 1.0f;
Arkku
  • 41,011
  • 10
  • 62
  • 84