Scale and Precision of NSDecimalNumber value - ios

Let us suppose I have a variable v of type NSDecimalNumber
let v = 34.596904 in its own format.
I want to know the precision and scale of this number, not the default one. I did not find any function in the NSDecimalNumber class which gives these values or maybe someone would like to throw some light on how it works.
precision = 8
scale = 6
precision is count of significant digits in number and scale is count of significant digit after decimal

This extension will give you the specific value for your only example:
extension Decimal {
var scale: Int {
return -self.exponent
}
var precision: Int {
return Int(floor(log10((self.significand as NSDecimalNumber).doubleValue)))+1
}
}
Usage:
let v: NSDecimalNumber = NSDecimalNumber(string: "34.596904")
print("precision=\((v as Decimal).precision)") //->precision=8
print("scale=\((v as Decimal).scale)") //->scale=6
But I cannot be sure if this generates expected results in all cases you have in mind, as you have shown only one example...
One more, in Swift, Decimal and NSDecimalNumber are easily bridgeable and you should better use Decimal as far as you can.

Related

toStringAsFixed() returns a round-up value of a given number in Dart

I want to get one decimal place of a double in Dart. I use the toStringAsFixed() method to get it, but it returns a round-up value.
double d1 = 1.151;
double d2 = 1.150;
print('$d1 is ${d1.toStringAsFixed(1)}');
print('$d2 is ${d2.toStringAsFixed(1)}');
Console output:
1.151 is 1.2
1.15 is 1.1
How can I get it without a round-up value? Like 1.1 for 1.151 too. Thanks in advance.
Not rounding seems highly questionable to me1, but if you really want to truncate the string representation without rounding, then I'd take the string representation, find the decimal point, and create the appropriate substring.
There are a few potential pitfalls:
The value might be so large that its normal string representation is in exponential form. Note that double.toStringAsFixed just returns the exponential form anyway for such large numbers, so maybe do the same thing.
The value might be so small that its normal string representation is in exponential form. double.toStringAsFixed already handles this, so instead of using double.toString, use double.toStringAsFixed with the maximum number of fractional digits.
The value might not have a decimal point at all (e.g. NaN, +infinity, -infinity). Just return those values as they are.
extension on double {
// Like [toStringAsFixed] but truncates (toward zero) to the specified
// number of fractional digits instead of rounding.
String toStringAsTruncated(int fractionDigits) {
// Require same limits as [toStringAsFixed].
assert(fractionDigits >= 0);
assert(fractionDigits <= 20);
if (fractionDigits == 0) {
return truncateToDouble().toString();
}
// [toString] will represent very small numbers in exponential form.
// Instead use [toStringAsFixed] with the maximum number of fractional
// digits.
var s = toStringAsFixed(20);
// [toStringAsFixed] will still represent very large numbers in
// exponential form.
if (s.contains('e')) {
// Ignore values in exponential form.
return s;
}
// Ignore unrecognized values (e.g. NaN, +infinity, -infinity).
var i = s.indexOf('.');
if (i == -1) {
return s;
}
return s.substring(0, i + fractionDigits + 1);
}
}
void main() {
var values = [
1.151,
1.15,
1.1999,
-1.1999,
1.0,
1e21,
1e-20,
double.nan,
double.infinity,
double.negativeInfinity,
];
for (var v in values) {
print(v.toStringAsTruncated(1));
}
}
Another approach one might consider is to multiply by pow(10, fractionalDigits), use double.truncateToDouble, divide by the power-of-10 used earlier, and then use .toStringAsFixed(fractionalDigits). That could work for human-scaled values, but it could generate unexpected results for very large values due to precision loss from floating-point arithmetic. (This approach would work if you used package:decimal instead of double, though.)
1 Not rounding seems especially bad given that using doubles to represent fractional base-10 numbers is inherently imprecise. For example, since the closest IEEE-754 double-precision floating number to 0.7 is 0.6999999999999999555910790149937383830547332763671875, do you really want 0.7.toStringAsTruncated(1) to return '0.6' instead of '0.7'?

Should I define all values as Double or mixed Double and Float when those values will be calculated frequently?

In my program, there are some decimal values that should be defined float respect to their range.
But, in several calculations (multiply), ranges might be larger than 10^38, so I need to convert them to Double before the calculation.
Say the values are
let a: Float // maximum: 10
let b: Float // maximum: 10^20
let c: Float // maximum: 10^30
and the calculations are like
func multiplyAB() -> Float {
return a * b
}
func multiplyBC() -> Double {
return Double(b) * Double(c)
}
let d = multiplyBC()
What bothers me is which one is better performance-wise?
Convert from Float to Double during calculation or define a, b, c as Double?
In other words, is converting from Float to Double a handy job to CPU (like realloc memory, handle precision and sort of things) comparing to calculate all numbers in Double?
BTW, why Apple use Double as the underlying value for CGFloat?
Maximum value for Float is 10^38, which is pretty large respect to iPhone screen sizes and pixels can't be float (10.111 and 10.11 make no difference, right?).
What's the reason behind that?
from
THE SWIFT PROGRAMMING LANGUAGE
"NOTE
Double has a precision of at least 15 decimal digits, whereas the precision of Float can be as little as 6 decimal digits. The appropriate floating-point type to use depends on the nature and range of values you need to work with in your code. In situations where either type would be appropriate, Double is preferred."

Find digit at index of NSDecimalNumber

Is it possible to find the number at an index of an NSDecimalNumber?
For example is something like this possible?
var a: NSDecimalNumber = 10.00123456789
var indexOfa = a(index: 6)
//indexOfa = 6 (6th position from the left)
I'm basically trying to make a custom rounding function that rounds down on x.xx5 and rounds up on x.xx6.
For example:
67.5558 is rounded to 67.55.
67.5568 is rounded to 67.56.
...Not a duplicate of rounding question. I'm asking specifically here how to find a digit at a specified index of an NSDecimalNumber.
Also the answer you linked doesn't answer my question. I can use the rounding behaviours for NSDecimalNumber but it rounds up on .5 I need it to round down on .5 and up on .6. I can create my own custom rounding function if I could find the index of the 2nd decimal number.
I have a solution that works. But its horrible. There's a few NSDecimalNumber extensions in there but you can probably guess what they are doing. And I'm sure theres a better way...
public func currencyRoundDown() -> NSDecimalNumber {
let rounded: NSDecimalNumber
let toThree = self.roundDown(3)
let lower = self.roundDown(2).adding(0.005)
if lower.moreThan(toThree) || lower.same(toThree) {
rounded = toThree.roundDown(2)
} else {
rounded = toThree.roundUp(2)
}
return rounded
}
Is it possible to find the number at an index of an NSDecimalNumber?
Let's consider how to do it with a Double. In this rendering, the "index" counts from the decimal point.
Multiply by 10 to the index, to bring the index digit into the 1s place. Now take the remainder modulo 10. That is the digit in question.
let d = 1234.56789 // the 5 is 1, the 6 is 2, the 7 is 3 ...
let place = 3
let shift = pow(10.0, Double(place)) * d
let digit = Int(shift) % 10 // 7

Getting weird value in Double

Hello i made a "Clicker" as a first project while learning swift i have an automated timer that is supposed to remove some numbers from other numbers but sometimes i get values like 0.600000000000001 and i have no idea why.
Here is my "Attack" function that removes 0.2 from the Health of a zombie.
let fGruppenAttackTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("fGruppenAttackTime"), userInfo: nil, repeats: true)
func fGruppenAttackTime() {
zHealth -= 0.2
if zHealth <= 0 {
zHealth = zSize
pPengar += pPengarut
}
...
}
And here is my attackZ button that is supposed to remove 1 from the health of the zombie
#IBAction func attackZ(sender: UIButton) {
zHealth -= Double(pAttack)
fHunger -= 0.05
fGruppenHunger.progress = Float(fHunger / 100)
Actionlbl.text = ""
if zHealth <= 0 {
zHealth = zSize
pPengar += pPengarut
}
}
Lastly here are the variables value:
var zHealth = 10.0
var zSize = 10.0
var pAttack = 1
var pPengar = 0
var pPengarut = 1
When the timer is on and the function is running and i click the button i sometimes get weird values like 0.600000000000001 and if i set the 0.2 in the function to 0.25 i get 0.0999999999999996 sometimes. I wonder why this happens and what to do with it.
In trojanfoe's answer, he shares a link that describes the source of the problem regarding rounding of floating point numbers.
In terms of what to do, there are a number of approaches:
You can shift to integer types. For example, if your existing values can all be represented with a maximum of two decimal places, multiply those by 100 and then use Int types everywhere, excising the Double and Float representations from your code.
You can simply deal with the very small variations that Double type introduces. For example:
If displaying the results in the UI, use NumberFormatter to convert the Double value to a String using a specified number of decimal places.
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 0 // or you might use `2` here, too
formatter.numberStyle = .decimal
print(formatter.string(for: value)!)
By the way, the NSNumberFormatter enjoys another benefit, too, namely that it honors the localization settings for the user. For example, if the user lives in Germany, where the decimal place is represented with a , rather than a ., the NSNumberFormatter will use the user's native number formatting.
When testing to see if a number is equal to some value, rather than just using == operator, look at the difference between two values and seeing if they're within some permissible rounding threshold.
You can use Decimal/NSDecimalNumber, which doesn't suffer from rounding issues when dealing with decimals:
var value = Decimal(string: "1.0")!
value -= Decimal(string: "0.9")!
value -= Decimal(string: "0.1")!
Or:
var value = Decimal(1)
value -= Decimal(sign: .plus, exponent: -1, significand: 9)
value -= Decimal(sign: .plus, exponent: -1, significand: 1)
Or:
var value = Decimal(1)
value -= Decimal(9) / Decimal(10)
value -= Decimal(1) / Decimal(10)
Note, I explicitly avoid using any Double values such as Decimal(0.1) because creating a Decimal from a fractional Double only captures whatever imprecision Double entails, where as the three examples above avoid that entirely.
It's because of floating point rounding errors.
For further reading, see What Every Computer Scientist Should Know About Floating-Point Arithmetic.
Squeezing infinitely many real numbers into a finite number of bits
requires an approximate representation. Although there are infinitely
many integers, in most programs the result of integer computations can
be stored in 32 bits. In contrast, given any fixed number of bits,
most calculations with real numbers will produce quantities that
cannot be exactly represented using that many bits. Therefore the
result of a floating-point calculation must often be rounded in order
to fit back into its finite representation. This rounding error is the
characteristic feature of floating-point computation.

Round function doesn't work as expected

I'm trying round a Double value with two decimal places:
var x = 0.68999999999999995
var roundX = round(x * 100.0) / 100.0
println(roundX) // print 0.69
If print the value is correct.. but the var value isn't that i expect, continue 0.68999999999999995
I need the Double value... not String like other StackOverflow answers :(
Floating point numbers like doubles do not have a number of decimal places. They store values in binary, and a value like .69 can't be represented exactly. It's just the nature of binary floating point on computers.
Use a number formatter, or use String(format:) as #KRUKUSA suggests
var x:Double = 0.68999999999999995
let stringWithTwoDecimals = String(format: "%.2f", x)
println(stringWithTwoDecimals)

Resources