Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
数値を文字列に整形する際の落とし穴とその解決策(iOSDC2024 ルーキーズLT) / iO...
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
glassfiber
August 23, 2024
Programming
900
0
Share
数値を文字列に整形する際の落とし穴とその解決策(iOSDC2024 ルーキーズLT) / iOSDC Japan 2024 Formatting Floating-Point Numbers
数値を文字列に整形する際に陥った落とし穴について、具体例を交えて紹介します。
glassfiber
August 23, 2024
More Decks by glassfiber
See All by glassfiber
Pythonで広がるXcodeデバッグの世界(pixiv App Night 2025/11/06) / pixiv App Night Xcode debugger with Python
glassfiber
0
50
pixiv App Night 20240523 NumberFormatterのハマり事例
glassfiber
0
180
Other Decks in Programming
See All in Programming
決定論 vs 確率論:Gemini 3 FlashとTF-IDFを組み合わせた「法規判定エンジン」の構築
shukob
0
120
Claude Code × Gemini × Ebitengine ゲーム制作素人WebエンジニアがGoでゲームを作った話
webzawa
0
160
PHPer、Cloudflare に引っ越す
suguruooki
1
110
10 Tips of AWS ~Gen AI on AWS~
licux
5
470
AIと共に生きる技術選定 2026
sgash708
0
110
Offline should be the norm: building local-first apps with CRDTs & Kotlin Multiplatform
renaudmathieu
0
230
JOAI2026 1st solution - heron0519 -
heron0519
0
150
The Less-Told Story of Socket Timeouts
coe401_
3
640
ハーネスエンジニアリングとは?
kinopeee
13
6.2k
Lightning-Fast Method Calls with Ruby 4.1 ZJIT / RubyKaigi 2026
k0kubun
3
1.3k
Cache-moi si tu peux : patterns et pièges du cache en production - Devoxx France 2026 - Conférence
slecache
0
310
アーキテクチャモダナイゼーションとは何か
nwiizo
19
5.5k
Featured
See All Featured
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
0
260
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
160
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
170
Design in an AI World
tapps
1
200
Producing Creativity
orderedlist
PRO
348
40k
Being A Developer After 40
akosma
91
590k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.4k
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
750
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
190
Between Models and Reality
mayunak
3
270
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
350
Transcript
glass fi ber (ϐΫγϒגࣜձࣾʣ Λจࣈྻʹܗ͢Δࡍͷ མͱ݀͠ͱͦͷղܾࡦ iOSDC Japan 2024 2024/08/23
Track A ϧʔΩʔζLT
00 iOS։ൃʹ6ϒϥϯΫͷ͋Δ ΤϯδχΞ͕෮ؼͨ͠Β ͷܗͰͲϋϚΓͨ͠
༻Λύʔηϯτදࣔ͢Δ • ετϨʔδͷ༻Λύʔηϯτදࣔ • ཁ: ͋Μ·Γͯ͘ݟʹ͍͘ͷͰ͍͍ײ͡ͷܻͰग़ͯ͠ཉ͍͠ (খୈ2Ґ͘Β͍ʁ) 00 ←͜Μͳײ͡ͷͭ
༻Λύʔηϯτදࣔ͢Δ • খୈ2Ґ·Ͱදࣔ͢ΔίʔυΛॻ͖·ͨ͠ • ҰݟΑͦ͞͏ʹݟ͑·͢ ͕ 00 let usageRate =
0.12345 let value = round(usageRate * 10000) / 10000 let percentValue = value * 100 print("\(percentValue)%") 12.35% ↑0.01%=0.0001ͳͷͰ ɹখୈ5ҐͰ࢛ࣺޒೖ͢Δ
• ʹΑͬͯظ͢ΔදࣔʹͳΒͳ͍͜ͱ͕໌ • ͨͱ͕͑0.523ͷͱ͖ • খҎԼͷܻ͕ͱͯ͘ͳͬͯ͠·͏ 52.300000000000004% ʮͳΜ͔ද͕͓͔ࣔ͘͠ͳΔʯͱ͍͏ใࠂ͕ 00 let
usageRate = 0.523 let value = round(usageRate * 10000) / 10000 let percentValue = value * 100 print("\(percentValue)%")
ͳΜͰ͜͏ͳΔͷ͔ • ුಈখͷޡ͕ࠩӨڹ • 2ਐͰදݱͰ͖ͳ͍ޡؚ͕ࠩ·ΕΔ • ࢛ࣺޒೖͨͣ͠ͳͷʹɺ100ഒͨ͠Β·ͨԼҐͷܻ͕ݱΕΔ… • ͱΓ͋͑ͣɺύʔηϯτද͍ࣔͨ͠ͱ͖ʹ100ഒͯ͠ͳΜͱ͔͠Α͏ͱ ͢ΔͷΑ͘ͳͦ͞͏
00
ܻΛࢦఆͨ͠ΒΑ͍ͷͰ • String(format:)ͰখҎԼͷܻΛࢦఆͨ͠ΒͲ͏Ͱ͠ΐ͏ • ݁Ռɺܻ͕ݻఆͳͷͰɺඌʹ0͕ͭ͘ • ͜ΕͰ͍͍͕ɺ͏ͪΐͬͱ͍͍ײ͡ʹ͍ͨ͠ • ඌͷ0ͳ͍ͨ͘͠ 00
let usageRate = 0.523 let value = round(usageRate * 10000) / 10000 print("\(String(format: "%.2f", value * 100))%") 52.30% ↑খҎԼ2ܻ
ͷܗͱ͍͑NumberFormatter • numberStyle = .percent ͱ͢Ε100ഒͤͣͱύʔηϯτදهʹͳΔ • ͢Β͍͠ 00 let
usageRate = 0.523 let formatter = NumberFormatter() formatter.numberStyle = .percent formatter.maximumFractionDigits = 2 if let str = formatter.string(from: NSNumber(value: usageRate)) { print(str) } 52.3% ←খҎԼ࠷େ2ܻ
খҎԼ2ܻʹͰ͖ͨɺ͚Ͳ • ཁ͕͖ͨ • 12.34%ͱ͍͏ͷ͍ɺͰे • Ͱ0.12%͜ͷ··খҎԼ2ܻग़ͯཉ͍͠ • →ʹΑܻͬͯΛ͍͍ײ͡ʹΓସ͍͑ͨ 00
let formatter = NumberFormatter() formatter.numberStyle = .percent func myFormat(_ value: Double) -> String { formatter.maximumFractionDigits = 2 return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) 12.34% 1.23% 0.12%
ͰɺܻΛΓସ͑ͯΈΔ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent func
myFormat(_ value: Double) -> String { formatter.maximumFractionDigits = switch value { case ..<1: 2 case ..<10: 1 default: 0 } return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) ←ͷൣғͰ߹͚ܻͯ͠ΛΓସ͑Δ 1%ະຬ / 1%Ҏ্10%ະຬ / 10%Ҏ্
ͰɺܻΛΓସ͑ͯΈΔ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent func
myFormat(_ value: Double) -> String { formatter.maximumFractionDigits = switch value { case ..<1: 2 case ..<10: 1 default: 0 } return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) ←ͷൣғͰ߹͚ܻͯ͠ΛΓସ͑Δ 1%ະຬ / 1%Ҏ্10%ະຬ / 10%Ҏ্ 12.34% 1.23% 0.12% มΘͬͯͳ͍…ʁ
ͰɺܻΛΓସ͑ͯΈΔ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent func
myFormat(_ value: Double) -> String { formatter.maximumFractionDigits = switch value { case ..<1: 2 case ..<10: 1 default: 0 } return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) 12.34% 1.23% 0.12% มΘͬͯͳ͍…ʁ ←ˋͷͰൺֱ͢Δؒҧ͍ (Α͘Δ)
ͰɺܻΛΓସ͑ͯΈΔ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent func
myFormat(_ value: Double) -> String { formatter.maximumFractionDigits = switch value { case ..<0.01: 2 case ..<0.1: 1 default: 0 } return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) ←ͷൣғͰ߹͚ܻͯ͠ΛΓସ͑Δ 1%ະຬ / 1%Ҏ্10%ະຬ / 10%Ҏ্
ͰɺܻΛΓସ͑ͯΈΔ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent func
myFormat(_ value: Double) -> String { formatter.maximumFractionDigits = switch value { case ..<0.01: 2 case ..<0.1: 1 default: 0 } return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) 12% 1.2% 0.12% ͍͍ײ͡ ←ͷൣғͰ߹͚ܻͯ͠ΛΓସ͑Δ 1%ະຬ / 1%Ҏ্10%ະຬ / 10%Ҏ্
ද͍͍ࣔײ͚ͩ͡Ͳɺίʔυ͕ؾʹೖΒͳ͍ • switchͰ͚ΔͷͳΜ͔ΠϚΠν 00 formatter.maximumFractionDigits = switch value { case
..<0.01: 2 case ..<0.1: 1 default: 0 }
Ͳ͏͍ͨ͠ͷ͔ߟ͑ͯΈΔ • ༗ޮࣈ͕2ܻɺͱ͍͏͜ͱͰΑͦ͞͏…ʁ • NumberFomatterʹ༗ޮܻΛࢦఆ͢Δػೳ͕͋Δ (maximumSigni fi cantDigits) 00 →
→ → 12% 1.2% 0.12% 0.12345 0.012345 0.0012345 ͜ͷͷͱ͖ ͜͏͍ͨ͠
༗ޮࣈ2ܻͰܗ͢Δ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent formatter.usesSignificantDigits
= true formatter.maximumSignificantDigits = 2 func myFormat(_ value: Double) -> String { return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) 12% 1.2% 0.12% ←༗ޮܻ2ܻʹઃఆ Αͦ͞͏…ʁ
খ͍͞ͷͱ͖ʹ͘ͳͬͯ͠·͏ͱࢦఠ͞ΕΔ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent formatter.usesSignificantDigits
= true formatter.maximumSignificantDigits = 2 func myFormat(_ value: Double) -> String { return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) print(myFormat(0.00012345)) print(myFormat(0.00000012345)) 12% 1.2% 0.12% 0.012% 0.000012%
খ͍͞ͷͱ͖ʹ͘ͳͬͯ͠·͏ • খ͍͞ͷͱ͖ʹ͔ͬ͠Γ༗ޮࣈ2ܻͰग़ΔͷͰ͘ͳΔ • খ෦ͷܻࢦఆ(maximumFractionDigits) จࣈྻͷ੍͕͞ݶ͞ ΕΔ͕ɺ༗ޮܻࢦఆ(maximumSigni fi cantDigits) ͦ͏Ͱͳ͍
• ͦΕͦ͏ 00 print(myFormat(0.00012345)) print(myFormat(0.00000012345)) 0.012% 0.000012%
খ͍ܻ͞Λ࢛ࣺޒೖͯ͠ղܾ • ࠷ॳʹ͍ͬͯͨɺখୈ5ҐͰͷ࢛ࣺޒೖΛ࠶ͼಋೖ͠·͢ • Α͏͍͍͘ײ͡ʹͰ͖·ͨ͠ 00 func myFormat(_ value: Double)
-> String { let roundedValue = round(value * 10000) / 10000 return formatter.string(from: NSNumber(value: roundedValue)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) print(myFormat(0.00012345)) print(myFormat(0.00000012345)) ←খୈ5ҐͰ࢛ࣺޒೖ 12% 1.2% 0.12% 0.01% 0%
func test_༻༰ྔ͕0ͷͱ͖ʹ0ύʔηϯτදهʹͳΔ() { let usage = 0 let limit =
1000000 * 1000 * 1000 let user = testUser(usage, limit) XCTAssertEqual(user.storageUsageFormatted, "0%") } func test_༻༰ྔ͕0_01ύʔηϯτະຬͷͱ͖ʹ0ύʔηϯτදهʹͳΔ let usage = 12 * 1000 * 1000 let limit = 1000000 * 1000 * 1000 let user = testUser(usage, limit) XCTAssertEqual(user.storageUsageFormatted, "0%") } func test_༻༰ྔ͕0_1ύʔηϯτະຬͷͱ͖ʹখ2Ґ·ͰͷදهʹͳΔ let usage = 123 * 1000 * 1000 let limit = 1000000 * 1000 * 1000 let user = testUser(usage, limit) XCTAssertEqual(user.storageUsageFormatted, "0.01%") } func test_༻༰ྔ͕1ύʔηϯτະຬͷͱ͖ʹখ2Ґ·ͰͷදهʹͳΔ() let usage = 1234 * 1000 * 1000 let limit = 1000000 * 1000 * 1000 let user = testUser(usage, limit) XCTAssertEqual(user.storageUsageFormatted, "0.12%") } func test_༻༰ྔ͕10ύʔηϯτະຬͷͱ͖ʹখ1Ґ·ͰͷදهʹͳΔ( ςετΛॻ͍ͯ҆৺͢Δ • Ͳ͏ͳͬͯ΄͍͔͠ΛͪΌ Μͱॻ͍ͯ֬ೝ͢Δ • ࣮ࡍͷετϨʔδ༰ྔͰද ࣔΛ֬ೝ͢Δͷඇݱ࣮త • ϋϚΔͳͲΛཏྻ͠ ͯɺظ͢ΔදࣔʹͳΔ͜ ͱΛ֬ೝ
·ͱΊ • ුಈখͷѻ͍͍͠ • Λύʔηϯτදهʹܗ͢Δࡍʹ100ഒͯ͠Ͳ͏ʹ͔͠Α͏ͱ͠ ͍͚ͯͳ͍ • NumberFormatterͳͲΛ͍ͬͯͩ͘͞ • ༗ޮܻͷࢦఆศར͕ͩɺจࣈྻͷ͞Λ੍ݶͯ͘͠Εͳ͍ͷͰҙ
͕ඞཁ • maximumFractionDigits ͱಉ͡Α͏ͳͷͱࢥͬͯ͠·͏ϫφ • ςετΛॻ͍ͯ҆৺͠Α͏ 00
ࣗݾհ glass fi ber ϐΫγϒגࣜձࣾ ৽نࣄۀ෦ iOSΤϯδχΞ • 20238݄ த్ೖࣾ
• Pastelaͱ͍͏͓ֆඳ͖ΞϓϦͷ։ൃʹैࣄ • લ৬ήʔϜ։ൃऀ • C++, C#Λओʹ༻ • 6લ·ͰiOS༻ήʔϜ࡞͍ͬͯͨ(iOS3ʙ7ͷ͜Ζ) • ϒϥϯΫΛຒΊΔͨΊमߦத 00
00 ͓·͚
ුಈখͷதΛݟΔ • ුಈখ: IEEE754 (Double : ഒਫ਼ɺFloat: ୯ਫ਼) • ුಈখͷܭࢉΛϏοτ୯ҐͰݟΕΔπʔϧ
• IEEE 754 Calculator (http://weitz.de/ieee/) 00
ුಈখͷதΛݟΔ • SwiftͰ .bitPatternͰුಈখͷදݱΛ ֬ೝͰ͖Δ • .exponentBitPatternͱ.signi fi candBitPatternͰࢦ෦ͱԾ෦Λݸผ ʹݟΔ͜ͱՄೳ
00 print("\(String(0.523.bitPattern, radix: 2))") print("\(String(0.523.exponentBitPattern, radix: 2))") print("\(String(0.523.significandBitPattern, radix: 2))") 11111111100000101111000110101001111110111110011101101100100011 1111111110 101111000110101001111110111110011101101100100011
None