Please tell me about generating fetch conditions for Float values.

Asked 2 years ago, Updated 2 years ago, 101 views

How do you generate fetch conditions for Float values?
The condition setting for the Ingteger value yielded the expected result, but the condition setting for the Float value did not yield the expected result.
Is there another way to search for Float values?
Or is there a problem with the Float value data model?

It's too late, but I'll add about what I wanted to do.
Data obtained by permanently retrieving the value of the range of accuracy of Swift's Float type using CoreData are obtained.
It is intended to be very simple and does not include:
- A numerical value calculation or the like is performed in a program, and the value obtained therein is made permanent.
- A numerical value calculation or the like is performed in a program, and the obtained value is defined as a key.
- A value outside the value of the precision range of the Float type is defined as a value of the Float type and as a permanent and retrieval key.
Values used as persistence and search keys are for Float-type precision range values only.

Please let me know if there are any defects in the code.

(After trying)

Condition | fetch value | Comments
===============+=============+=====================================
fvalue<0.05 | 0.05 or less | ** Not less than 0.05.0.05 gets fetched too. Why?**
fvalue<=0.05 | 0.05 or less | No problem as expected.
fvalue==0.05 | | **Nothing was missing.Why don't you get 0.05?**
fvalue>0.05 | 0.06 or higher | No problem as expected.
fvalue> = 0.05 | 0.06 or greater | ** 0.05 or greater.I can't get 0.05. Why?**
---------------+-------------+-------------------------------------
value <5 | <5 | No problem as expected.
ivalue<=5 | 5 or less | No problem as expected.
value == 5 | 5 | No problem as expected.
ivalue>5 | 6 or higher | No problem as expected.
ivalue>=5 | 5 or higher | No problem as expected.

(Core model)

Entities:
Entity | Attribute | Type
======+=========+======
Pin | name | String
      | fvalue | Float
      | value | Integer 16

(code)

[AppDelegate.swift]
func applicationDidFinishLaunching(_aNotification:Notification){
    for in 0..<10 {
        autoreleasepool {(result) in
            let pin = NSEntityDescription.insertNewObject(forEntityName: "Pin", into:managedObjectContext) as!Pin
            pin.name="pin\(i)"
            pin.fvalue = NSNumber (value: Float(i)*0.01)
            pin.value=NSNumber(value:i)

            do{
                try managedObjectContext.save()
            } catch{
                let ns error = error as NSError
                NSAApplication.shared().presentError(nserror)
            }
        }
        managedObjectContext.refreshAllObjects()
    }

    do{
        let fetchRequest=NSFetchRequest<NSFetchRequestResult>(entityName: "Pin")
        fetchRequest.predicate=NSPredicate (format: "fvalue<0.05")
        // fetchRequest.predicate=NSPredicate(format: "value<5")
        let sortDiscriptor= NSSortDescriptor(key: "name", ascending:true)
        fetchRequest.sortDescriptors = [sortDiscriptor]
        let pins = try managedObjectContext.fetch(fetchRequest) as! [Pin]
        for pin in pins {
            print("name:\(pin.name!), fvalue:\(pin.fvalue!), value:\(pin.ivalue!)")
        }
    } catchlet error as NSError {
        fatalError("\(error)")
    }
}

swift swift3 coredata

2022-09-30 21:21

2 Answers

It's not a question of how to fetch Core Data, it's a question of binary floating point numbers.

You can reproduce the same results without using Core Data.

(It's not 0..<10 but 0...10 because it's just right for discussing binary floating point issues.)

import Foundation

let floatArr: [NSNumber] = (0...10).map {NSNumber(value: Float($0)*0.01)}
print(floatArr)//-> [0,0.01,0.02,0.03,0.04,0.05,0.06,0.07,0.08,0.09,0.09999]
varpredicate = NSPredicate (format: "SELF<0.05")
print(floatArr.filter {predicate.validate(with:$0)})//->[0,0.01,0.02,0.03,0.04,0.05]
predict = NSPredicate (format: "SELF<=0.05")
print(floatArr.filter {predicate.validate(with:$0)})//->[0,0.01,0.02,0.03,0.04,0.05]
predict = NSPredicate (format: "SELF==0.05")
print(floatArr.filter {predicate.evaluate(with:$0)})//->[]
predict = NSPredicate (format: "SELF>0.05")
print(floatArr.filter {predicate.validate(with:$0)})//-> [0.06, 0.07, 0.08, 0.09, 0.0999999]
predict = NSPredicate (format: "SELF>=0.05")
print(floatArr.filter {predicate.validate(with:$0)})//-> [0.06, 0.07, 0.08, 0.09, 0.0999999]

Decimal numbers, such as 0.01, that can be expressed as finite digits in decimal are also infinitely decimal (circular).(_ does not contain any numbers, it simply represents the range of iterations.)

0.00_000010100011110111_0000100001110111_0000100001110111_0000100001110111_...

The Float type uses 24 bits (23 bits following the first 1), and the remaining digits are rounded, so this value cannot be accurately represented by the Float type.

You may have noticed that the 10th value above is displayed as 0.0999999, not 0.1, but the 5th value displayed as 0.05 is not really 0.05.

print(Float(5)*0.01-0.05)//->-3.72529e-09

(In the above example, 0.05 does not represent the exact theoretical value 0.05, but Float(5)*0.01 does not match the value of the type Float closest to the decimal notation 0.05.)

When viewing the NSNumber type as a decimal string, rounding seems to hold the correct values at first glance, but the value as the Float type does not actually accurately represent those values.

Use debugPrint to show these minor differences in decimal display as much as possible.

for in 0...10 {
    debugPrint (Float(i)*0.01)
}

0.0
0.009999978
0.019999996
0.029999993
0.03999991
0.04999997
0.059999987
0.0700000003
0.079999982
0.089999961
0.09999994

Is there a problem with the or Float value data model?

For applications that require precise decimal designation of boundaries, as shown above, NSDecimalNumber instead of NSNumber on the code side without using binary floating point float or double.


2022-09-30 21:21

As you pointed out that there is a problem with the real value of binary numbers, after trial and error, we decided to correct the code mentioned in the question as follows.In this case, it seems to be working, but there must be a lot of problems with accuracy.

(Set value to coredata)

pin.fvalue=NSNumber(value:Float(Double(i)*0.01))

(Generate predicate)

 fetchRequest.predicate=NSPredicate (format: "fvalue=%f", 0.05)

Here are some of the trials and errors

As you pointed out that there is a problem with handling binary numbers, I tried handling real numbers.

(1) Similar to the code presented in the question

let f1 = Float(5)*0.01
let fn1 = NSNumber (value: f1)

When I looked at the debugger, it was as follows.
f1Float 0.04999997
fn1NSNumber Float (0.050000)

(2) For constants

let f2 = 0.05
let fn2 = NSNumber (value: f2)

f2Double 0.05000000000000000003
fn2NSNumber Double (0.05)

It seems to be treated as Double.

(3) In the results of an integer and a real number operation.The integer and real number operations of variables require type conversion, but constants can be calculated.

let f3 = 5*0.01
let fn3 = NSNumber (value: f3)

f3 Double 0.05000000000000000003
fn3NSNumber Double (0.05)
(1)In , Float and constant operations were set to Float, but the result was Double.

(4) Based on the results of (3), we decided to adjust to Double instead of Float.

let f4 = Double (5) * 0.01
let fn4 = NSNumber (value: f4)

f4 Double 0.05000000000000000003
fn4NSNumber Double (0.05)

 (5) Float to NSNumber.
let f5 = Double (5) * 0.01
let fn5 = NSNumber (value: Float(f5))

f5 Double 0.05000000000000000003
fn5NSNumber Float (0.050000)


2022-09-30 21:21

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.