Hello everyone,
To follow the spec, ‘ox-icalendar’ needs to map priorities onto the
range 1–9, and currently does this by taking ‘floor’ of a ratio to
ensure the highest priorities map to 1. This leads to an asymmetric
distribution, and about double [1] the average absolute error compared
to rounding, e.g. see the table below.
I propose that we round here instead, and have attached a first draft
of a patch doing so.
|---+----+--------+--------+-------+-------|
| H | L | εfloor | εround | εceil | ratio |
|---+----+--------+--------+-------+-------|
| 1 | 1 | 0.000 | 0.000 | 0.000 | 1.000 |
| 1 | 2 | 0.000 | 0.000 | 0.000 | 1.000 |
| 1 | 3 | 0.000 | 0.000 | 0.000 | 1.000 |
| 1 | 4 | 0.250 | 0.167 | 0.250 | 1.497 |
| 1 | 5 | 0.000 | 0.000 | 0.000 | 1.000 |
| 1 | 6 | 0.333 | 0.200 | 0.333 | 1.665 |
| 1 | 7 | 0.286 | 0.190 | 0.286 | 1.505 |
| 1 | 8 | 0.375 | 0.214 | 0.375 | 1.752 |
| 1 | 9 | 0.000 | 0.000 | 0.000 | 1.000 |
| 1 | 10 | 0.400 | 0.222 | 0.400 | 1.802 |
| 1 | 16 | 0.437 | 0.233 | 0.438 | 1.876 |
| 1 | 32 | 0.469 | 0.242 | 0.469 | 1.938 |
| 1 | 64 | 0.484 | 0.246 | 0.484 | 1.967 |
| 1 | 91 | 0.484 | 0.247 | 0.484 | 1.960 |
|---+----+--------+--------+-------+-------|
#+TBLFM: $1=1;%d::$3='(test/ox-icalendar $2
#'floor);N%.3f::$4='(test/ox-icalendar $2
#'round);N%.3f::$5='(test/ox-icalendar $2 #'ceiling);N%.3f::$6=if(max($3,$4,$5)
< 1e-5,1,$3/min($4,$5));%.3f
(defun test/ox-icalendar (N f)
"Report the average absolute difference between 1+8(n-1)/(N-1) [2]
and the function f applied to the same for n = 1,…,N."
(/ (seq-reduce (lambda (e n)
(let ((frac (if (= N 1)
1
(1+ (* 8 (/ (- n 1.0) (- N 1.0)))))))
(+ e (abs (- frac (funcall f frac))))))
(number-sequence 1 N) 0)
N))
[1] In the limit L-H → ∞ the floor, rounding, and ceiling errors tend
to 1/2, 1/4, and 1/2, respectively.
[2] 9 - 8(L-p)/(L-H) = 1 + 8(p-H)/(L-H)
Thanks,
--
Jacob S. Gordon
[email protected]
Please don’t send me HTML emails or MS Office/Apple iWork documents.
https://useplaintext.email/#etiquette
https://www.fsf.org/campaigns/opendocumentFrom 64a12329ca654871800b420e09986b545d10dfd0 Mon Sep 17 00:00:00 2001
From: "Jacob S. Gordon" <[email protected]>
Date: Tue, 12 May 2026 16:21:00 -0400
Subject: [PATCH v1] ox-icalendar: Round priorities to the nearest integer
On average, this leads to about half the absolute error compared to
taking the floor.
* lisp/ox-icalendar.el (org-icalendar--vtodo): Round priorities to the
nearest integer 1-9 and refactor formula.
* etc/ORG-NEWS (Miscellaneous): Announce change.
---
etc/ORG-NEWS | 6 ++++++
lisp/ox-icalendar.el | 6 +++---
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index aef019552..c53df500c 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -249,6 +249,12 @@ tangled files. This behavior no longer applies when =:tangle=
specifies multiple targets; in that case, absolute links are always
used and the variable is ignored.
+*** ~ox-icalendar~ now rounds priorities instead of taking the integer part
+
+Exporting to =iCalendar= requires mapping of priorities to the range
+1--9. On average, rounding of fractional priorities leads to an
+absolute error half as large compared to taking the integer part.
+
* Version 9.8
** Important announcements and breaking changes
diff --git a/lisp/ox-icalendar.el b/lisp/ox-icalendar.el
index 272e3cf5d..721733175 100644
--- a/lisp/ox-icalendar.el
+++ b/lisp/ox-icalendar.el
@@ -1021,9 +1021,9 @@ (defun org-icalendar--vtodo
org-priority-default)))
(if (= org-priority-lowest org-priority-highest)
1
- (floor (- 9 (* 8. (/ (float (- org-priority-lowest pri))
- (- org-priority-lowest
- org-priority-highest))))))))
+ (round (1+ (* 8. (/ (float (- pri org-priority-highest))
+ (- org-priority-lowest
+ org-priority-highest))))))))
(format "STATUS:%s\n"
(if (eq (org-element-property :todo-type entry) 'todo)
"NEEDS-ACTION"
--
Jacob S. Gordon
[email protected]
Please don’t send me HTML emails or MS Office/Apple iWork documents.
https://useplaintext.email/#etiquette
https://www.fsf.org/campaigns/opendocument