How should I store percentage values in Core Data that are read off UISliders?
Sliders are my app's primary user interaction element (go figure...). I use them to record percentage values which are then stored as Reading
s in my Core Data store. Based on the nature of percentage values, I would store them as decimal values between 0 and 1, and set the sliders to have a range from 0 to 1 and fire their value-changed actions continuously. When I need to display them I fetch them from the data store and display them as typical percentage values, e.g. 67%
.
Now here's the catch: the value
property of UISlider
is of type float
. That means I will run into rounding errors from the get-go. I'm nutty for accuracy, so I'm hoping to reduce the error margin as much as possible when I deal with them throughout my app.
What options are there for managing the percentage values which I'm reading off my sliders, storing in Core Data and displaying in the rest of my app? Or better yet, which of these options that I've come up with would be best for my app in terms of code maintainability, memory usage/performance and accuracy of the values obtained?
Use raw floats or doubles
Actually this is BAD, obviously because of rounding errors. Even 0.64 turns into 0.63 at some point in time (I've tested with my sliders and these come up a lot). With a difference potentially as large as 0.01, my app is definitely intolerant of this. Why am I even writing this as an option anyway?
Set slider range to between 0 and 100, read them, round up/down and store as integers
This is an interesting choice given that I'll only ever display the values as
##%
and not0.##
. I won't need to perform arithmetic on these values either, so doing this looks alright:// Recording and storing // Slider range is 0 to 100 int percentage = (int) floor(slider.value); // Or ceil() [reading setValue:[NSNumber numberWithInt:percentage]]; // Value is an integer // Displaying NSNumber *val = reading.value; [readingLabel setText:[NSString stringWithFormat:@"%d%%", [val intValue]]];
This is not going to completely take away the rounding errors when I first read them off my sliders (they're still
float
s!), butfloor()
orceil()
should cut it, I think. I'm not entirely certain though; maybe someone here can provide more insight, which is the point of me asking this question.Convert to, and store as,
NSDecimalNumber
objectsI've taken a look at the
NSDecimalNumber
class — percentages are base-10 factors after all — but given that it produces immutable objects, I'm not too sure I like the idea of writing long method calls all over my code repeatedly and creating a crapton of autoreleased objects everywhere.Even creating
NSDecimalNumber
s out of primitives is a pain. Currently I can only think of doing this:// Recording and storing // Slider range i开发者_开发问答s 0 to 1 NSDecimalNumber *decimalValue = [NSDecimalNumber decimalNumberWithString: [NSString stringWithFormat:@"%0.2f", slider.value]]; [reading setValue:decimalValue]; // Value is a decimal number
Multiplying them by 100 since I display them as
##%
(like I said above) also gets really tedious and creates unnecessary extra objects which are then converted toNSString
s. I would go on, but I'd likely turn this post into a rant which is definitely not what I'm going for.With that said, semantically
NSDecimalNumber
should make the most sense because, as I said above, I believe percentages are meant to be stored as decimal values between 0 and 1. However, looking at how my app works with percentage values, I don't know whether to choose multiplying them by 100 and storing as integers or working withNSDecimalNumber
.
You can probably see that I'm already leaning slightly toward the second option (x100, use integers) because it's just more convenient and looks to be slightly better for performance. However, I'd like to see if anyone thinks the third is a better (more future proof?) one.
By the way, I don't mind modifying my data model to change the data type of the value
attribute in my Reading
entity, and modifying my existing code to accommodate the changes. I haven't done too much just yet because I've spent the rest of my time worrying about this.
Which of the above options do you think I should go for?
If all you need to do is read from a slider, save the values, and present them, dealing with primitive integers sourced from the scaled, rounded float slider input would be what I'd recommend. A slider isn't all that precise an input method, anyway, so your users won't notice if the rounding doesn't exactly match the pixel placement of the slider head.
I'd use the same sort of reasoning here as you do when determining the number of significant digits from a calculation that takes real-world data as its input (like a temperature reading, etc.). There's no sense in reporting a calculation to four significant digits if your input source only has a precision of two digits.
NSDecimals and NSDecimalNumbers are needed when you want to perform more extended calculations on decimal values, but want to avoid floating point errors. For example, I use them when running high-precision calculations in my application or if I need to manipulate currency in some way. Of the two, I stick with NSDecimal for performance reasons, using NSDecimalNumber on the occasions that I need to interact with Core Data or need to import numerical values into NSDecimal structs.
In this case, it seems like NSDecimal would be overkill, because you're not really adjusting the slider values in any way, just displaying them onscreen. If you did need to perform later manipulations (cutting in half, running through a formula, etc.), I'd recommend rounding the slider to an integer representation, creating an NSDecimal struct from that, performing calculations as NSDecimals, and then using the string output routines for NSDecimal / NSDecimalNumber to display the result onscreen. You can then easily save the calculated value as an NSDecimalNumber in Core Data or as a string representation in SQLite or another file.
Also, I wouldn't worry too much about performance on simple calculations like this until something like Instruments tells you they are a hotspot. Odds are, these calculations with NSDecimal, etc. won't be.
精彩评论