“% ไม่พร้อมใช้งาน: ใช้ truncatingRemainder แทน” หมายความว่าอย่างไร


105

ฉันได้รับข้อผิดพลาดต่อไปนี้เมื่อใช้โค้ดสำหรับส่วนขยายฉันไม่แน่ใจว่าพวกเขาขอให้ใช้ตัวดำเนินการอื่นหรือแก้ไขค่าในนิพจน์ตามการค้นหาทางอินเทอร์เน็ต

ข้อผิดพลาด:% ไม่พร้อมใช้งาน: ใช้ truncatingRemainder แทน

รหัสส่วนขยาย:

extension CMTime {
    var durationText:String {
        let totalSeconds = CMTimeGetSeconds(self)
        let hours:Int = Int(totalSeconds / 3600)
        let minutes:Int = Int(totalSeconds % 3600 / 60)
        let seconds:Int = Int(totalSeconds % 60)

        if hours > 0 {
            return String(format: "%i:%02i:%02i", hours, minutes, seconds)
        } else {
            return String(format: "%02i:%02i", minutes, seconds)
        }
    }
}

ข้อผิดพลาดเกิดขึ้นเมื่อตั้งค่าตัวแปรนาทีและวินาที


1
ฉันคิดว่า CMTimeGetSeconds คืนค่า float
zombie

3
หมายความว่า%โอเปอเรเตอร์ไม่พร้อมใช้งานและคุณควรพิจารณาใช้truncatingRemainderวิธีการแทน
แมตต์

1
คุณไม่สามารถใช้ modulo ได้Float64แต่เปิดIntอย่างเดียว ดังนั้น: let minutes:Int = Int(totalSeconds) % 3600 / 60; let seconds:Int = Int(totalSeconds) % 60เป็นวิธีที่ถูกต้อง
holex

@holex. คุณผิด. คุณสามารถใช้ตัวดำเนินการแบบโมดูโลในตัวถูกดำเนินการกับชนิดที่สอดคล้องกับไม่เพียงBinaryInteger Int
Peter Schorn

@PeterSchorn ขอบคุณสำหรับการแก้ไขความคิดเห็น 3 ปี - โปรโตคอลนั้นไม่สามารถใช้งานได้ในเวลานั้น
holex

คำตอบ:


174

CMTimeGetSeconds()ส่งคืนตัวเลขทศนิยม ( Float64aka Double) ใน Swift 2 คุณสามารถคำนวณส่วนที่เหลือของการหารทศนิยมเป็น

let rem = 2.5 % 1.1
print(rem) // 0.3

ใน Swift 3 สิ่งนี้ทำได้ด้วย

let rem = 2.5.truncatingRemainder(dividingBy: 1.1)
print(rem) // 0.3

ใช้กับรหัสของคุณ:

let totalSeconds = CMTimeGetSeconds(self)
let hours = Int(totalSeconds / 3600)
let minutes = Int((totalSeconds.truncatingRemainder(dividingBy: 3600)) / 60)
let seconds = Int(totalSeconds.truncatingRemainder(dividingBy: 60))

อย่างไรก็ตามในกรณีนี้จะง่ายกว่าในการแปลงระยะเวลาเป็นจำนวนเต็มตั้งแต่แรก:

let totalSeconds = Int(CMTimeGetSeconds(self)) // Truncate to integer
// Or:
let totalSeconds = lrint(CMTimeGetSeconds(self)) // Round to nearest integer

จากนั้นบรรทัดถัดไปจะทำให้ง่ายขึ้น

let hours = totalSeconds / 3600
let minutes = (totalSeconds % 3600) / 60
let seconds = totalSeconds % 60

24

ตัว%ดำเนินการโมดูลัสถูกกำหนดไว้สำหรับชนิดจำนวนเต็มเท่านั้น สำหรับประเภททศนิยมคุณต้องเจาะจงมากขึ้นเกี่ยวกับประเภทของการแบ่ง IEEE 754 / พฤติกรรมที่เหลือที่คุณต้องการดังนั้นคุณต้องเรียก method: อย่างใดอย่างหนึ่งremainderหรือtruncatingRemainder. (หากคุณกำลังทำเลขทศนิยมคุณต้องสนใจเรื่องนี้จริง ๆ และสิ่งอื่น ๆ อีกมากมายหรือคุณอาจได้รับผลลัพธ์ที่ไม่คาดคิด / ไม่ดี)

ถ้าคุณจริงตั้งใจจะทำโมดูลัสจำนวนเต็มคุณจะต้องแปลงค่าตอบแทนของจำนวนเต็มก่อนที่จะใช้CMTimeGetSeconds %(โปรดทราบว่าถ้าคุณทำเช่นนั้นคุณจะตัดเศษเสี้ยววินาทีออกไป ... ขึ้นอยู่กับว่าคุณกำลังใช้CMTimeที่ใดซึ่งอาจสำคัญคุณต้องการนาที: วินาที: เฟรมหรือไม่)

ขึ้นอยู่กับว่าคุณต้องการนำเสนอCMTimeค่าใน UI ของคุณอย่างไรอาจเป็นการดีกว่าถ้าแยกค่าวินาทีและส่งต่อไปNSDateFormatterหรือNSDateComponentsFormatterเพื่อให้คุณได้รับการสนับสนุนภาษาที่เหมาะสม


10

นำไวยากรณ์โมดูโลอย่างง่ายกลับมาใน swift 3:

ไวยากรณ์นี้ได้รับการแนะนำในรายชื่ออีเมลที่รวดเร็วอย่างเป็นทางการของ Apples ที่นี่แต่ด้วยเหตุผลบางประการพวกเขาจึงเลือกใช้ไวยากรณ์ที่หรูหราน้อยกว่า

infix operator %%/*<--infix operator is required for custom infix char combos*/
/**
 * Brings back simple modulo syntax (was removed in swift 3)
 * Calculates the remainder of expression1 divided by expression2
 * The sign of the modulo result matches the sign of the dividend (the first number). For example, -4 % 3 and -4 % -3 both evaluate to -1
 * EXAMPLE: 
 * print(12 %% 5)    // 2
 * print(4.3 %% 2.1) // 0.0999999999999996
 * print(4 %% 4)     // 0
 * NOTE: The first print returns 2, rather than 12/5 or 2.4, because the modulo (%) operator returns only the remainder. The second trace returns 0.0999999999999996 instead of the expected 0.1 because of the limitations of floating-point accuracy in binary computing.
 * NOTE: Int's can still use single %
 * NOTE: there is also .remainder which supports returning negatives as oppose to truncatingRemainder (aka the old %) which returns only positive.
 */
public func %% (left:CGFloat, right:CGFloat) -> CGFloat {
    return left.truncatingRemainder(dividingBy: right)
}

เคล็ดลับการย้ายข้อมูลแบบรวดเร็ว 3 แบบนี้เป็นส่วนหนึ่งของคู่มือการย้ายข้อมูลแบบรวดเร็ว 3 ที่ครอบคลุมยิ่งขึ้นพร้อมข้อมูลเชิงลึกมากมาย (35k loc / 8-days of migration) http://eon.codes/blog/2017/01/12/swift-3-migration /


1
A นี้ใช้ได้ดีให้ข้อมูลที่น่าสนใจและพยายามตอบคำถามด้วย
Jakub Truhlář

3
@Jakub Truhlář ... แมน ๆ Thx. IMO นี่คือการแก้ไขการย้ายข้อมูลที่รวดเร็ว 3 ที่ดีที่สุดของฉัน ไม่อยากเชื่อว่าคนจะโหวตให้ Modulo เป็นแนวคิดที่สำคัญและมีอยู่ในหนังสือรหัสทุกเล่มที่มีเลขคณิต การทำให้เป็นแบบ verbose นั้นไม่สมเหตุสมผลเนื่องจาก Arithmetic ใน code ควรเขียนให้กระชับที่สุด เมื่อความสามารถในการรับรู้ของเราในการเข้าใจเลขคณิตเพิ่มขึ้นเมื่อคุณสามารถเห็นภาพที่สมบูรณ์ซึ่งตรงข้ามกับการเข้าใจความหมายของตัวแปรแต่ละตัว การตั้งชื่อตัวแปรที่ครอบคลุมของ IMO มีความสำคัญในตรรกะทางธุรกิจ แต่ไม่ใช่เลขคณิตซึ่งตรงกันข้าม
eonist

2
@GitSync Modulo เป็นแนวคิดที่สำคัญ แต่มีอยู่สำหรับจำนวนเต็มเท่านั้น คุณควรเข้าใจความแตกต่าง
Sulthan

5
@GitSync การทำงานของโมดูโลมีเฉพาะสำหรับจำนวนเต็ม คุณกำลังพูดถึงส่วนที่เหลือ ค่าทศนิยมมีสองประเภทของเศษเหลือ นั่นเป็นเหตุผลที่ Swift ตัดสินใจให้การดำเนินการอย่างชัดเจน การคำนวณเศษที่เหลือเป็นจำนวนเต็ม (การตัดเศษที่เหลือ ) สำหรับค่าสองค่าเป็นเรื่องปกติไม่มาก
Sulthan

1
@GitSync มีremainder(dividingBy:)และtruncatingRemainder(dividingBy:)คุณอาจต้องการอ่านเอกสารสำหรับทั้งคู่ นอกจากนี้โปรดดูคำถามเดียวกันสำหรับ C ++ stackoverflow.com/questions/6102948/…
Sulthan

2

ฉันพบว่าสิ่งต่อไปนี้ใช้ได้ใน Swift 3:

    let minutes = Int(floor(totalSeconds / 60))
    let seconds = Int(totalSeconds) % 60

ที่totalSecondsเป็นTimeInterval( Double)


3
การผสมพื้นและรอบไม่ใช่ความคิดที่ดีเช่นสำหรับtotalSeconds = 59.8รหัสของคุณคำนวณ 0 นาทีและ 0 วินาที
Martin R

ใช่คุณพูดถูก. ในความเป็นจริงroundไม่จำเป็นเลย
benwiggy

2

ไม่จำเป็นต้องสร้างตัวดำเนินการโมดูโลแยกต่างหากสำหรับตัวเลขทศนิยมเว้นแต่คุณคิดว่าจะทำให้โค้ดปลอดภัยขึ้น คุณสามารถโอเวอร์โหลดตัว%ดำเนินการเพื่อยอมรับตัวเลขทศนิยมได้ดังนี้:

func %<N: BinaryFloatingPoint>(lhs: N, rhs: N) -> N {
    lhs.truncatingRemainder(dividingBy: rhs)
}

การใช้งาน

let a: Float80 = 10
let b: Float80 = 3
print(a % b)

ตอนนี้คุณสามารถใช้%กับเลขทศนิยมสองตัวของ tye เดียวกันได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.