"Binary operator > cannot be applied to two Float operands" error - swift2

I have a conditional that looks like:
if sqrtf(powf(startTouch.x - (touches.first as! UITouch).locationInView(self.view).y, 2) + powf(startTouch.y-(touches.first as! UITouch).locationInView(self.view).y, 2)) > dragThreshold as! Float {
self.drag = false;
}
I am getting an error that says Binary operator > cannot be applied to two Float operands. I cannot understand why I can't check if a float is greater than another float. What is this error trying to tell me?

The members of a CGPoint are of type CGFloat, and assuming you are on a 64-bit architecture the native type used to store a CGFloat is Double - try treating those calculated values as Doubles (use sqrt() and pow() instead of the Float versions)... using dragThreshold as a Double as well

Using latest Xcode 7 beta 4 and SpriteKit's function override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) I did break this big chunk to smaller ones like this:
let dragThreshold: Int = 1
let startTouch = CGPointZero
let pow1 = powf(Float(startTouch.x - touches!.first!.locationInView(self.view).y), 2.0)
let pow2 = powf(Float(startTouch.y-touches!.first!.locationInView(self.view).y), 2.0)
let sq = sqrtf(pow1 + pow2)
if sq > Float(dragThreshold) {
self.drag = false;
}
This works. Basically added more conversions for powf arguments, changed 2 to 2.0
General advice - if you get some strange errors, try to break down your expression into smaller ones to isolate issue.

Related

Swift 4: Strange Double and Float behaviour

Basically the problem is that in this example:
let d1 = NSNumber(value: 1.4);
let d2 = d1.doubleValue;
let f1 = NSNumber(value: Float(1.4));
let f2 = d1.floatValue;
d1 results 1.4
d2 results 1.3999999999999999
f1 results 1.4
f2 results 1.3999999999999998
Does anyone know why is that?
I'm trying to parse JSON file like:
{"name": "something", "version": 1.4}
with the following code:
let json = try (JSONSerialization.jsonObject(with: someData) as? [String: Any])!;
let version: Double = (json["version"] as! NSNumber).doubleValue;
OR
let version: Double = json["version"] as! Double;
OR
let version: Double = json["version"] as! Float;
And I just can't get 1.4...
Rounding the number is not a solution for me, because I want to write back this number to JSON file, that will be parsed by other programs/languages and needs to be exactly 1.4 in the file.
Any suggestions?
UPDATE: The problem is only with 1.1 and 1.4. There is no problem with 1.2, 1.3, 1.5
UPDATE 2: Serialization code:
let jsonDict: Dictionary<String,Any> = [
"name" : name,
"version" : version
];
let data = try? JSONSerialization.data(withJSONObject: jsonDict, options: []);
let jsonString = String(data:data, encoding:.utf8);
Ok, just to finalise the discussion.
At the end Decimal type did the trick. So I changed all variable references to Decimal and NOT NSDecimalNumber, because I got error that it doesn't comply with Codable and Decodable protocols. Maybe there is a workaround for this, but the easiest solution is just to stick with Decimal.
I would like to thanks to #JamesBucanek and #EricPostpischil for joining the discussion and help resolving this issue !!!
I had the same story.
I need to sent/receive float values using API endpoint. So I followed advice and changed Double to Decimal. It worked fine for encoding data, but not for decoding.
let value = "0.0006"
let decimal = Decimal(string: value)!
print(decimal) // 0.0006 OK
let jsonData = try JSONEncoder().encode(decimal)
print(String(data: jsonData, encoding: .utf8)!) // 0.0006 OK
let decocdedDecimal = try JSONDecoder().decode(Decimal.self, from: jsonData)
print(decocdedDecimal) // 0.0005999999999999998976 NOOOOOO!!!
However decode to Double works fine.
let decocdedDouble = try JSONDecoder().decode(Double.self, from: jsonData)
print(decocdedDouble) // 0.0006 OK
Also as was mentioned in answers above - Decimal should be inited with String to be encoded correctly
let decimal = Decimal(0.0006)
print(decimal) // 0.0005999999999999998976, it will be encoded same way
And ofcourse Double encoding not working as expected, otherwise we didn't have such issue.
// 0.00059999999999999995 for all cases ofcourse
print(String(data: try JSONEncoder().encode(0.0006), encoding: .utf8)!)
print(String(data: try JSONEncoder().encode(decocdedDouble), encoding: .utf8)!)
print(String(data: try JSONEncoder().encode(Double(value)!), encoding: .utf8)!)
So my dirty solution for now is to use wrapper for Double values. It will decode value as Double, but encode as Decimal(string:)
struct CodableDouble: Codable {
var value: Double
init(_ value: Double) {
self.value = value
}
init(from decoder: Decoder) throws {
value = try decoder.singleValueContainer().decode(Double.self)
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let decimal = Decimal(string: "\(value)") ?? 0
try container.encode(decimal)
}
}
But I still don't understand what is the correct way to handle this issue. (except not using float values)

Displaying float to a variable number of decimal places in Swift

Is there a simple way of displaying a float or double to a relevant number of decimal places in Swift.
For example, an iOS app using SI units, which can be altered depending on the property desired, and converted through up to 6 orders of magnitude depending on desired inputs and output. Therefore it needs to display not only 1mg to 1000 micrograms, but also the other way around - i.e 1 microgram = 0.001 mg.
I can easily format a string as follows:
textFieldFoo.text = NSString(format: "%.1f mg", bar) as String
However, if the user were to convert from 1mcg to 0.001mg, this would display as
0.0 mg
Yet, to include up to 6 decimal places to encompass all common possibilities would lead to an unwieldy, ugly looking UI.
Is there a simple way to format a string, in order to include a float/ double where it is displayed to a relevant number of decimal places/ significant figures? I'm sure, given time and enough boilerplate code, that I could pyramid if/ else it to get a result, but that's frankly inelegant.
There's NSMAssFormatter but it doesn't go all the way down to microgram. It was designed to format human-level weight.
You can roll your own by subclassing NSNumberFormatter:
enum MassUnit: Double {
case Microgram = 1e-6
case Milligram = 1e-3
case Gram = 1
case Kilogram = 1e3
static let allUnits: [MassUnit] = [.Microgram, .Milligram, .Gram, .Kilogram]
var unitAbbreviation: String {
get {
switch self {
case .Microgram: return "mcg"
case .Milligram: return "mg"
case .Gram: return "g"
case .Kilogram: return "kg"
}
}
}
}
class MyMassFormatter: NSNumberFormatter {
func bestFitStringForWeightInGrams(weight: Double) -> String {
var selectedString = self.stringFromNumber(weight)!
var selectedUnit = MassUnit.Gram
// Pick the unit that results in the shortest string
for unit in MassUnit.allUnits {
if let str = self.stringFromNumber(weight / unit.rawValue)
where str.characters.count < selectedString.characters.count {
selectedString = str
selectedUnit = unit
}
}
return selectedString + selectedUnit.unitAbbreviation
}
}
Usage:
let formatter = MyMassFormatter()
formatter.format = "0.######"
print(formatter.bestFitStringForWeightInGrams(0.000001)) // 1mcg
print(formatter.bestFitStringForWeightInGrams(0.005)) // 5mg
print(formatter.bestFitStringForWeightInGrams(2500)) // 2.5kg
print(formatter.bestFitStringForWeightInGrams(1234.5)) // 1234.5g
Formatting to Significant Figures using Swift
What you want is the ability to format to a fixed number of significant figures, rather than a fixed number of decimal places. A good swift option to solve this is using class extensions, with a little maths to decide how many decimal places to show based on the magnitude of the number.
The example below extends the Double class to enable formatting to a fixed number of significant figures and uses either float notation or scientific notation depending on the magnitude of the number.
import Foundation
//extension to format a Double to a fixed number of significant figures
extension Double {
func sigFigs(_ numberOfSignificantFigures: Int) -> String {
let mag = log10(abs(self))
let intMag = Int(mag)
if mag >= 0 {
if intMag < numberOfSignificantFigures {
return String(format: "%.\(numberOfSignificantFigures - intMag - 1)f",self)
}
else {
return String(format: "%.\(numberOfSignificantFigures - 1)e",self)
}
}
else {
if -intMag < numberOfSignificantFigures {
return String(format: "%.\(numberOfSignificantFigures)f",self)
}
else {
return String(format: "%.\(numberOfSignificantFigures - 1)e",self)
}
}
}
}
Usage
let num1 = 1234.5678
let num2 = 12.345678
let num3 = 0.0012345678
let num4 = 1234567.8
print(num1.sigFigs(6))
print(num1.sigFigs(2))
print(num2.sigFigs(6))
print(num2.sigFigs(2))
print(num3.sigFigs(6))
print(num3.sigFigs(2))
print(num4.sigFigs(6))
print(num4.sigFigs(2))
Output
1234.57
1.2e+03
12.3457
12
0.001235
1.2e-03
1.23457e+06
1.2e+06
If I understand you correctly you are:
using Swift
working with SI units
trying to display floating points
trying to avoid boilerplate and possibly magic numbers
You should definitely use Apple's Measurement which is :
A numeric quantity labeled with a unit of measure, with support for unit conversion and unit-aware calculations.
and MeasurementFormatter which is :
A formatter that provides localized representations of units and measurements.
MeasurementFormatter uses a NumberFormatter to format the quantity of a measurement.
NumberFormatters's usesSignificantDigits property is set to false by default but :
Set this property to true to format numbers according to the significant digits configuration specified by the minimumSignificantDigits and maximumSignificantDigits properties. By default, the minimum number of significant digits is 1, and the maximum number of significant digits is 6.
Here's an example of what you can do with masses
let micrograms = Measurement(value: 1, unit: UnitMass.micrograms) // 1.0 µg
let nanograms = micrograms.converted(to: .nanograms) // 1000.0000000000001 ng
let picograms = micrograms.converted(to: .picograms) // 1000000.0 pg
let milligrams = micrograms.converted(to: .milligrams) // 0.001 mg
let centigrams = micrograms.converted(to: .centigrams) // 0.0001 cg
let decigrams = micrograms.converted(to: .decigrams) // 1e-05 dg
let grams = micrograms.converted(to: .grams) // 1e-06 g
let kilograms = micrograms.converted(to: .kilograms) // 1e-09 kg
let ounces = micrograms.converted(to: .ounces) // 3.527399072294044e-08 oz
let pounds = micrograms.converted(to: .pounds) // 2.2046244201837776e-09 lb
let stones = micrograms.converted(to: .stones) // 1.574731232746851e-10 st
let formatter = MeasurementFormatter()
formatter.numberFormatter.usesSignificantDigits = true
formatter.unitOptions = .providedUnit
formatter.string(from: nanograms) // "1 000 ng"
formatter.string(from: picograms) // "1 000 000 pg"
formatter.string(from: micrograms) // "1 µg"
formatter.string(from: milligrams) // "0,001 mg"
formatter.string(from: centigrams) // "0,0001 cg"
formatter.string(from: decigrams) // "0,00001 dg"
formatter.string(from: grams) // "0,000001 g"
formatter.string(from: kilograms) // "0,000000001 kg"
formatter.string(from: ounces) // "0,000000035274 oz"
formatter.string(from: pounds) // "0,00000000220462 lb"
formatter.string(from: stones) // "0,000000000157473 st"

Double is not convertible to UInt8, Swift error

Error: Double is not convertible to UInt8 (at the last line of code)
var beweegsnelheid = NSTimeInterval()
var random3 = CGFloat(arc4random_uniform(100))
var beweegsnelheidmax = beweegsnelheid * 1.2
var beweegsnelheidmin = beweegsnelheid * 0.8
beweegsnelheid = NSTimeInterval(beweegsnelheidmin + (random3/100 * (beweegsnelheidmax - beweegsnelheidmin)))
what am I doing wrong?
You're trying to multiply a CGFloat by a Double (CGFloat is a typedef for Float).
random3/100 * (beweegsnelheidmax - beweegsnelheidmin)
Unfortunately Swift currently requires explicit type conversions all over the place.
var beweegsnelheid = NSTimeInterval()
var random3 = Float(arc4random_uniform(100))
var beweegsnelheidmax = Float(beweegsnelheid * 1.2)
var beweegsnelheidmin = Float(beweegsnelheid * 0.8)
beweegsnelheid = NSTimeInterval(beweegsnelheidmin + (random3/100 * (beweegsnelheidmax - beweegsnelheidmin)))
Just make sure you use either Float or Double throughout and you shouldn't have a problem.
Would be more obvious if it would complain that CGFloat is not convertible to Double. Swift compiler errors are sometimes really bad.
All of your values are Double, except random3. Make it a Double as well and your code should work.
let random3 = Double(arc4random_uniform(100))

Using a number that is smaller then 1

Hei, I am trying my first steps in Swift coding, and I am trying to build a calculator. It should divide things though 0.5 for example, but I am getting a error message because it cant read 0.5 just full digits like 5. How can I enter number that are smaller then 1? I am very thankful for help! here is my code:
#IBAction func findBudget(sender: AnyObject) {
var enteredBudget = enterBudget.text.toInt ()
var myBudget = enteredBudget! / 0.5
resultMy.text = "My Budget: \(myBudget)"
}
You can covert your Int to a Double:
var myBudget = Double(enteredBudget!) / 0.5
You do this by getting the Double value from your string rather than an Int value. An easy way to do this is to initialize an NSString from your text and use its doubleValue property, or you could cast it to be an NSString.
let enteredBudget = NSString(string: textField.text).doubleValue
let myBudget = enteredBudget / 0.5
println(myBudget)

cocos2d - CCActionTween doesn't work

I have roundPath and BoxPath and I would like to do:
id modifyPath = [CCActionTween actionWithDuration:2 key:#"path" from:roundPath to:boxpath];
but I have the error "incompatible type of argument 3 of 'actionWithDuration:key:from:to:' Ho w can I solve this please ? sorry for my english I'm french :/
I presume that "path" is a CGPoint type. In that case you can't use CCActionTween because it only works on built-in data types like BOOL, char, int, float, double but not C structs. CGPoint is a C struct defined as {float x; float y;}.
You can't use CCActionTween with path.x and path.y either. That's because you can't do this in Objective-C:
node.position.x = 10; // ERROR
You could however subclass and add two float properties myX and myY. You can tween both individually and assign them to the position in an update method every frame:
self.position = CGPointMake(myX, myY);

Resources