Can't find or work out a solution to this that works with Dart.
I've already tried:
1. toStringAsFixed() - this rounds
2. double.parse(number.toStringAsFixed()) - this rounds
3. num - num % 0.01 - this rounds
I've found some solutions on SO but they either use functions that aren't available on Flutter/Dart or didn't work for me.
Any help would be greatly appreciated.
To build on #Irn beautiful Answer.
The function below lets you specify how many fractional digits / how many decimal places you want
double truncateToDecimalPlaces(num value, int fractionalDigits) => (value * pow(10,
fractionalDigits)).truncate() / pow(10, fractionalDigits);
Example
truncateToDecimalPlace(4321.92385678, 3) // 4321.923
Or my favorite way is to use Dart extensions
extension TruncateDoubles on double {
double truncateToDecimalPlaces(int fractionalDigits) => (this * pow(10,
fractionalDigits)).truncate() / pow(10, fractionalDigits);
}
This lets you call the function on a double object like thus
Usage
4321.92385678.truncateToDecimalPlaces(3); // 4321.923
Try
double truncateToHundreths(num value) => (value * 100).truncate() / 100;
There will be cases where you lose precision, but that requires you to use almost all 53 bits of double precision in the number (when multiplying by 100 loses precision to begin with).
Going trough strings is a significant overhead that I would avoid if possible, unless you actually expect to convert the number to a string anyway afterwards.
(value * 100).truncateToDouble() / 100
Example:
var lat = 29.4562
lat * 100 = 2945.62
2945.62 truncateToDouble() = 2945
2945 / 100 = 29.45
I have used the below method
String toFixed(double value, [int decimalPlace = 1]) {
try {
String originalNumber = value.toString();
List<String> formattedNumber = originalNumber.split('.');
return "${formattedNumber[0]}.${formattedNumber[1].substring(0, decimalPlace)}";
} catch (_) {}
return value.toString();
}
Related
This question already has answers here:
Is floating point math broken?
(31 answers)
How do you round a double in Dart to a given degree of precision AFTER the decimal point?
(28 answers)
Closed 12 months ago.
Dart calculations are off. We're running into rounding issues (financial app)
Here an example
Dartpad output
Hours : 7.5
Rate : 19.61
Val (Hours * Rate) : 147.075
Val * 100 (should be 14707.5) : 14707.499999999998
Round Val (now rounds down) : 14707
Val / 100 : 147.07 (should have been 147.08)
This is causing rounding errors displaying values to 2 decimal places.
Is there a way to accomplish this accurately in dart?
This is a floating point precision thing, nothing really to do with the language itself. There are many ways around this problem. See mine below.
void main() {
var vHours = 7.5;
print('Hours : $vHours');
var vRate = 19.61;
print('Rate : $vRate');
var vValue = vHours * vRate;
print('Val (Hours * Rate) : $vValue');
print('');
var vMul = (vValue * 100);
var vMulString = vMul.toStringAsFixed(2);
var vMulParsed = num.parse(vMulString);
print('Val * 100 (should be 14707.5) : $vMulParsed');
var vMulRounded = vMulParsed.round();
print('Round Val (now rounds down) : $vMulRounded');
var vDiv = vMulRounded / 100;
print('Val / 100 : $vDiv (should have been 147.08)');
}
i want to map a continuous range of double values [10.0,20.0] to a byte in the range of [100,200];
The following must apply (even when converting back and forth multiple times):
convertToByte(convertToDouble(y)) == y
Thanks.
With help of the comments to the question we're using the following code:
int convertToByte(
double initial,
double minValue,
double maxValue,
int minByte,
int maxByte,
) {
double value = initial - minValue;
double valueRange = maxValue - minValue;
int byteRange = maxByte - minByte;
double valueSteps = valueRange / byteRange;
double byte = (value / valueSteps);
return (minByte + byte.round()).clamp(minByte, maxByte);
}
This does not provide a solution for the specified test, but a deterministic answer for a specific value.
When converting a byte back to a value and vice versa multiple times the output always stays the same.
This is what we needed for our application.
Three part question:
How to find 2 user created horizontal lines on a chart by name and return the price of each.
Then determine which HLine was crossed by the price most recently to determine trend direction.
Calculate Fibonacci levels based on prices and direction
double value = ObjectGetDouble(0,nameOfHLine,OBJPROP_PRICE1);
this is your value if you have name of the object, if you dont have it - need to loop over all objects:
string name;
for(int i=ObjectsTotal()-1;i>=0;i--){
name = ObjectName(i);
if(ObjectType(name)!=OBJ_HLINE) continue;
}
Working example of Fibonacci object that can be edited by the user and printing of fibonacci levels.
#include <ChartObjects/ChartObjectsFibo.mqh>
CChartObjectFibo *Fibo;
int OnInit()
{
Fibo = new CChartObjectFibo();
#Create object and set some defaults
if(!Fibo.Create(0,"Fibonacci",0,Time[5],Open[5],Time[0],Open[0]))
{
return(INIT_FAILED);
}
# Allow user to drag object
Fibo.Selectable(true);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
delete Fibo;
}
void OnTick()
{
string level_description;
double level_value;
string printString="Fibonacci Levels - ";
# Get the two anchor prices
double p1 = Fibo.GetDouble(OBJPROP_PRICE,0);
double p2 = Fibo.GetDouble(OBJPROP_PRICE,1);
# Calculate range
double range=MathAbs(p1-p2);
for(int i=0;i<Fibo.LevelsCount();i++)
{
level_description=Fibo.LevelDescription(i);
# Calculate price of level
level_value=(p2>p1)?p2-range*Fibo.LevelValue(i):p2+range*Fibo.LevelValue(i);
printString=StringFormat("%s %s:%.5f",printString,level_description,level_value);
}
Print(printString);
}
Difficult to understand exactly what you are after, not sure if you are trying to find the graphical objects or just calculate levels based on the prices. Assuming you have the price of the two horizontal lines, the following structure and function can be used to calculate Fibonacci levels. (price 1 is earlier in time than price 2).
Calculation based on formula found here
struct FibLevel {
double retrace38;
double retrace50;
double retrace61;
double extension61;
double extension100;
double extension138;
double extension161;
};
void FibLevel(double price1, double price2,FibLevel &fiblevel)
{
double range = MathAbs(price1-price2);
fiblevel.retrace38 =(price1<price2)?price2-range*0.382:price1+range*0.382;
fiblevel.retrace50 =(price1<price2)?price2-range*0.500:price1+range*0.500;
fiblevel.retrace61 =(price1<price2)?price2-range*0.618:price1+range*0.618;
fiblevel.extension61 =(price1<price2)?price2+range*0.618:price1-range*0.618;
fiblevel.extension100=(price1<price2)?price2+range :price1-range;
fiblevel.extension138=(price1<price2)?price2+range*1.382:price1-range*1.382;
fiblevel.extension161=(price1<price2)?price2+range*1.618:price1-range*1.618;
}
I am trying to Multiply
self.tipLable.text = String("\((enterBillAmountTextField.text! as NSString).integerValue * (middleTextField.text! as NSString).integerValue * (0.01))")
But getting error Binary operator * cannot be applied to operands of type Int and Double
I am taking values form UITextfields. How to do this multiplication?
extension Double {
// Convert Double to currency
var currency: String {
let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
return formatter.stringFromNumber(self) ?? "0"
}
}
tipLable.text = [enterBillAmountTextField, middleTextField].reduce(0.01) { $0 * (Double($1.text!) ?? 0) }.currency
A slightly shorter and clearer solution. Added a "currency" extension so it can still be done in one line :).
This works
self.tipLable.text = String("\( Double((enterBillAmountTextField.text! as NSString).integerValue) * Double((middleTextField.text! as NSString).integerValue) * 0.01)")
Swift doesn't know how to multiply an Int and a Double. Should the result be an Int or a Double?
Swift won't do implicit type conversion between different operands.
If you want your result to be a Double, both operands should be a Double. Then convert the Double to a String.
While this can all be concisely expressed in one single very long line, perhaps it's more readable and maintainable if you break it out into separate lines:
let subTotal = Double(billAmountTextField.text!) ?? 0
let percent = (Double(middleTextField.text!) ?? 0) * 0.01
let tip = subTotal * percent
self.tipLable.text = String(format: "%.2f", tip) // Handle rounding
The answer you gave is going to bring nightmare to you in some moment.
Try to keep yourself doing things in a way you can guarantee that you are going to be able to test it and that you/or others are going to be able to understand what you are doing there.
/**
Use this function to calculate tip, useful for later testing
- returns: Double Value of the tip you should give
*/
func calculateTip(billAmount billAmount:Double, middleValue:Double) -> Double {
/// Actually calculate Tip if everything is OK
return billAmount * middleValue * 0.01
}
Then in your #IBAction make sure you have correct data before asking
your function for a tip
/// If you have bill data, obtain Double value stored there,
/// if something fails, you should return nil
guard let billAmountText = enterBillAmountTextField.text, billAmount = Double(billAmountText) else {
return
}
/// If you have middle value data, obtain Double value stored there,
/// if something fails, you should return nil
guard let middleText = middleTextField.text, middleValue = Double(middleText) else {
return
}
Then you can call that function
let tip = calculateTip(billAmount: billAmount, middleValue: middleValue).description
//and return in proper format
tipLabel.text = String(format: "%.2f", tip)
It is recommended to round the decimals but i am facing an scenario where i just need to cut down the precision.
Output: 15.96 to 16.0
Desired output: 15.96 to 15.9
Codes:
var value: AnyObject = dict.valueForKey("XXX")!
var stringVal = NSString(format:"%.1f", value.floatValue)
I thought this will be simple but found tricky. Your thoughts on this is highly appreciated.
Use a NSNumberFormatter and configure its rounding mode accordingly:
let formatter = NSNumberFormatter()
formatter.maximumFractionDigits = 1
formatter.roundingMode = .RoundDown
let s = formatter.stringFromNumber(15.96)
// Result: s = "15.9"
If you need to use the rounded number in future math operations, you can use the following function:
func roundToPlaces(_ value: Double, places: Int, rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero) -> Double {
let divisor = pow(10.0, Double(places))
return (value * divisor).rounded(rule) / divisor
}
You can then call it with
var value: AnyObject = dict.valueForKey("XXX")!
var rounded = roundToPlaces(value.doubleValue, places: 1, rule: .down)
var stringVal = "\(rounded)"
What this actually did was the following:
15.96 * 10.0 = 159.6
floor(159.6) = 159.0
159.0 / 10.0 = 15.9
Caveat: This won't help in situations where you're using scientific precision, i.e.
1.49850e0 --> 1.4e0 // (5 places --> 1 place)
1.39e10 --> 1.3e10 // (3 places --> 1 place)
It will treat all numbers as e0
[update 2018-08-09]
Since it seems like my answer is getting some views, I would like to point out that rounding floating-point numbers by division can introduce errors because of how floating-point numbers are stored in memory. as user #mattt has pointed out elsewhere:
floor(1.5679999 * 1000) / 1000 == 1.5669999999999999
(if you want to get your math on, this paper is a great primer on numbers and computers)
If you need that level of precision, use fixed-point numbers instead. Swift provides the Decimal type for this purpose.
The important thing is to understand your problem. If you're working with money or sensor data, you probably want Decimals. If you're working with computer graphics, you can go with Floats.
Try using this:
var test : AnyObject = "15.96"
var rounded_down = floorf(test.floatValue * 10) / 10;
print(rounded_down)
Here's an updated answer in Swift 5 based on #Clafou 's answer. You can use it as an extension to any data type. Example
extension Double {
func cutToDecimalPlace(_ decimalPlaces: Int) -> String{
let formatter = NumberFormatter()
formatter.maximumFractionDigits = decimalPlaces
formatter.roundingMode = .down
return formatter.string(from: NSNumber(value: self)) ?? ""
}
}
And you can call it like this
let priceValueString = "24.124"
let updatedPriceValue = priceValueString.doubleValue.cutToDecimalPlace(1)
Output will be
24.1